我之前喜欢玩一款游戏:全民飞机大战,而且有点痴迷其中,如果你想站在游戏的第一阶梯,便需要不断的练技术练装备,但是腾讯的游戏一般而言是有点恶心的,他会不断的出新飞机、新装备、新宠物,所以,很多时候你一个飞机以及装备还没满级,新的装备就又出来了,并且一定是更强!

于是很多人便直接抛弃当前的飞机与装备,追求更好的,这个时候如果是人民币玩家或者骨灰级大神玩家的话,基本可以很快站在世界的顶端,一者是装备好,一者是技术好,但是我不愿意投入太多钱,也不愿意投入过多精力,于是在一套极品装备满级后会积累资源,因为一代之间变化不会太大,到第二代甚至第三代才开始换飞机换装备,也基本处于了第一阶梯,一直到一次游戏大更新,直接导致我当前的飞机与装备完全二逼了,我当时一时脑热投入了所有资源去刷新的极品装备,最后闹的血本无归,于是便删除了该游戏,一年时间付诸东流!!!

再回过头来看最近两年前端的变化,单是一个前端工程化工具就变了几次,而且新出来的总是叫嚷着要替换之前的,grunt->gulp->webpack->es6

再看前端框架的一些产量:backbone、angularJS、react、canJS、vueJS......

真有点乱花渐欲迷人眼的意思,似乎前端技术也开始想要坑前端玩家,因为人家会了新技能,你就落后了,于是很多技术沉淀已经足够的大神便直接在团队使用某一技术,带领团队组员深入了解了该技术的好,并大势宣传新技术。

很多人在这种情况下就中招了!他们可能会抛弃现有技术栈,直接跟风新的技术,在现有装备都没满级的情况下又去刷新装备,如果哪天一次游戏玩法大更新,大神依旧一套极品装备在那风骚,而炮灰仓库中积累着一箩筐低等级的极品装备,却没有可用的,不可谓不悲哀!

一门技术从入门到精通,是需要时间的,在有限的时间中要学习那么多的新技术,还得落地到实际工作中,而每一次新技术的落地都是对曾经架构的否定与推翻,这个成本不可谓不高,对一些创业团队甚至是致命的。工作中也没那么多时间让你折腾新东西,所以一定是了解清楚了一门再去学习其它的,不要半途而废也不要盲目选型。

我最近回顾了这几年所学,可以说技术栈没有怎么更新,但是我对我所习的每一个技术基本上进入了深入的了解:

① 在MVVM还不太火的时候使用了MVC框架一直到最近,对为什么要使用这种模式,这种模式的好处有了比较深入的了解,并且已经碰到了更复杂的业务逻辑

② 当一个页面过于复杂时(比如1000多行代码的订单填写页),我能通过几年沉淀,将之拆分为多个业务组件模块,保持主控制器的业务清晰,代码量维护在500行之内,并且各子模块业务也清晰,根据model进行通信

③ 使用Grunt完成前端工程化,从构建项目,到打包压缩项目,到优化项目,总的来说无往不利

④ ......

就编程方法,思维习惯,解决问题的方法来说,与两年前有了很大的变化,而且感觉很难提高了。于是我认识到,就现有的装备下,可能已经玩到极限了,可能到了跟风的时候了,而时下热门的ReactJS似乎是一个很好的切入点,React一端代码多端运行的噱头也足够。

初识ReactJS

我最初接触ReactJS的时候,最火的好像是angular,React Native也没有出现,看了他的demo,对其局部刷新的实现很感兴趣。结果,翻看源码一看洋洋洒洒一万多行代码,于是马上便退却了。却不想现在火成了这般模样,身边学习的人多,用于生产的少,我想幕后必然有黑手在推动!也可以预测的是,1,2年后会有更好的框架会取代他,可能是原团队的自我推翻,也有可能是Google公司又新出了什么框架,毕竟前端最近几年才开始真正富客户端,还有很长的路要走。当然,这不是我们关心的重点,我们这里的重点是Hello world。

ReactJS的Hello World是这样写的:

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <script src="build/react.js" type="text/javascript"></script>
 5     <script src="build/JSXTransformer.js" type="text/javascript"></script>
 6 </head>
 7 <body>
 8     <div id="example">
 9     </div>
10     <script type="text/jsx">
11       React.render(
12         <h1>Hello, world!</h1>,
13         document.getElementById('example')
14       );
15     </script>
16 </body>
17 </html>
<div id="example"><h1 data-reactid=".0">Hello, world!</h1></div>

React一来就搞了一个标新立异的地方:jsx(js扩展),说实话,这种做法真的有点大手笔,最初的这种声明式标签写法,在我脑中基本可以追溯到5年前的.net控件了,比如gridview与datalist组件。

在text/jsx中的代码最初不会被浏览器理会,他会被react的JSXTransformer编译为常规的JS,然后浏览器才能解析。这里与html模板会转换为js函数是一个道理,我们有一种优化方案是模板预编译,即:

在打包时候便将模板转换为js函数,免去在线解析的过程,react当然也可以这样做,这里如果要解析的话,会是这个样子:

1 React.render(
2   React.createElement("h1", null, "Hello, world!"),
3   document.getElementById('example')
4 );

因为render中的代码可以很复杂,render中的代码写法就是一种语法糖,帮助我们更好的写表现层代码:render方法中可以写html与js混杂的代码:

1 var data = [1,2,3];
2 React.render(
3     <h1>Hello, {data.toString(',')}!</h1>,
4     document.getElementById('example')
5 );
1 var data = [1,2,3];
2 React.render(
3     <h1>{
4         data.map(function(v, i) {
5             return <div>{i}-{v}</div>
6         })
7     }</h1>,
8     document.getElementById('example')
9 );

所以,react提供了很多类JS的语法,JSXTransformer相当于一个语言解释器,而解析逻辑长达10000多行代码,这个可不是一般屌丝可以碰的,react从这里便走出了不平常的路,而他这样做的意义是什么,我们还不知道。

标签化View

react提供了一个方法,将代码组装成一个组件,然后像HTML标签一样插入网页:

 1 var Pili = React.createClass({
 2     render: function() {
 3         return <h1>Hello World!</h1>;
 4     }
 5 });
 6 
 7 React.render(
 8     <Pili />,
 9     document.getElementById('example')
10 );

所谓,声明试编程,便是将需要的属性写到标签上,以一个文本框为例:

<input type="text" data-type="num" data-max="100" data-min="0" data-remove=true />

我们想要输入的是数字,有数字限制,而且在移动端输入的时候,右边会有一个X按钮清除文本,这个便是我们期望的声明式标签。

react中,标签需要和原始的类发生通信,比如属性的读取是这样的:

 1 var Pili = React.createClass({
 2     render: function() {
 3         return <h1>Hello {this.props.name}!</h1>;
 4     }
 5 });
 6 
 7 React.render(
 8     <Pili name='霹雳布袋戏'/>,
 9     document.getElementById('example')
10 );
11 
12 //Hello 霹雳布袋戏!

上文中Pili便是一个组件,标签使用法便是一个实例,声明式写法最终也会被编译成一般的js方法,这个不是我们现在关注的重点。

由于class与for为关键字,需要使用className与htmlFor替换

通过this.props对象可以获取组件的属性,其中一个例外为children,他表示组件的所有节点:

 1 var Pili = React.createClass({
 2     render: function() {
 3         return (
 4             <div>
 5                 {
 6                     this.props.children.map(function (child) {
 7                       return <div>{child}</div>
 8                     })
 9                 }
10             </div>
11         );
12     }
13 });
14 
15 React.render(
16     <Pili name='霹雳布袋戏'>
17         <span>素还真</span>
18         <span>叶小钗</span>
19     </Pili>
20     ,
21     document.getElementById('example')
22 );
1 <div id="Div1">
2     <div data-reactid=".0">
3         <div data-reactid=".0.0">
4             <span data-reactid=".0.0.0">素还真</span></div>
5         <div data-reactid=".0.1">
6             <span data-reactid=".0.1.0">叶小钗</span></div>
7     </div>
8 </div>

PS:return的语法与js语法不太一样,不能随便加分号

如果想限制某一个属性必须是某一类型的话,便需要设置PropTypes属性:

1 var Pili = React.createClass({
2     propType: {
3         //name必须有,并且必须是字符串
4         name:  React.PropTypes.string.isRequired
5     },
6     render: function() {
7         return <h1>Hello {this.props.name}!</h1>;
8     }
9 });

如果想设置属性的默认值,则需要:

 1 var Pili = React.createClass({
 2     propType: {
 3         //name必须有,并且必须是字符串
 4         name:  React.PropTypes.string.isRequired
 5     },
 6     getDefaultProps : function () {
 7         return {
 8             title : '布袋戏'
 9         };
10     },
11     render: function() {
12         return <h1>Hello {this.props.name}!</h1>;
13     }
14 });

我们仍然需要dom交互,我们有时也需要获取真实的dom节点,这个时候需要这么做:

 1 var MyComponent = React.createClass({
 2   handleClick: function() {
 3     React.findDOMNode(this.refs.myTextInput).focus();
 4   },
 5   render: function() {
 6     return (
 7       <div>
 8         <input type="text" ref="myTextInput" />
 9         <input type="button" value="Focus the text input" onClick={this.handleClick} />
10       </div>
11     );
12   }
13 });

事件触发的时候通过ref属性获取当前dom元素,然后可进行操作,我们这里看看返回的dom是什么:

<input type="text" data-reactid=".0.0">

看来是真实的dom结构被返回了,另外一个比较关键的事情,便是这里的dom事件支持,需要细读文档:http://facebook.github.io/react/docs/events.html#supported-events

表单元素,属于用户与组件的交互,内容不能由props获取,这个时候一般有状态机获取,所谓状态机,便是会经常变化的属性。

 1 var Input = React.createClass({
 2   getInitialState: function() {
 3     return {value: 'Hello!'};
 4   },
 5   handleChange: function(event) {
 6     this.setState({value: event.target.value});
 7   },
 8   render: function () {
 9     var value = this.state.value;
10     return (
11       <div>
12         <input type="text" value={value} onChange={this.handleChange} />
13         <p>{value}</p>
14       </div>
15     );
16   }
17 });
18 
19 React.render(<Input/>, document.body);

组件有其生命周期,每个阶段会触发相关事件可被用户捕捉使用:

Mounting:已插入真实 DOM
Updating:正在被重新渲染
Unmounting:已移出真实 DOM

一般来说,我们会为一个状态发生前后绑定事件,react也是如此:

componentWillMount()
componentDidMount()
componentWillUpdate(object nextProps, object nextState)
componentDidUpdate(object prevProps, object prevState)
componentWillUnmount()
此外,React 还提供两种特殊状态的处理函数。
componentWillReceiveProps(object nextProps):已加载组件收到新的参数时调用
shouldComponentUpdate(object nextProps, object nextState):组件判断是否重新渲染时调用

根据之前的经验,会监控组件的生命周期的操作的时候,往往都是比较高阶的应用了,我们这里暂时不予关注。

好了,之前的例子多半来源于阮一峰老师的教程,我们这里来一个简单的验收,便实现上述只能输入数字的文本框:

 1 var NumText = React.createClass({
 2     getInitialState: function() {
 3         return {value: 50};
 4     },
 5     propTypes: {
 6         value: React.PropTypes.number
 7     },
 8     handleChange: function (e) {
 9         var v = parseInt(e.target.value);
10         if(v > this.props.max || v < this.props.min  ) return;
11         if(isNaN(v)) v = '';
12         this.setState({value: v});
13     },
14     render: function () {
15         return (
16             <input type="text" value={this.state.value} onChange={this.handleChange} />
17         );
18     }
19 });
20 
21 React.render(
22   <NumText min="0" max="100" />,
23   document.body
24 );

通过以上学习,我们对React有了一个初步认识,现在我们进入其todolist,看看其是如何实现的

此段参考:阮一峰老师的入门教程,http://www.ruanyifeng.com/blog/2015/03/react.html

TodoMVC

入口文件

TodoMVC为MVC框架经典的demo,难度适中,而又可以展示MVC的思想,我们来看看React此处的入口代码:

 1 <!doctype html>
 2 <html lang="en" data-framework="react">
 3 <head>
 4     <meta charset="utf-8">
 5     <title>React • TodoMVC</title>
 6     <link rel="stylesheet" href="node_modules/todomvc-common/base.css">
 7     <link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
 8 </head>
 9 <body>
10     <section class="todoapp">
11     </section>
12     <script src="node_modules/react/dist/react-with-addons.js"></script>
13     <script src="node_modules/react/dist/JSXTransformer.js"></script>
14     <script src="node_modules/director/build/director.js"></script>
15     <script src="js/utils.js"></script>
16     <script src="js/todoModel.js"></script>
17 
18     <script type="text/jsx" src="js/todoItem.jsx"></script>
19     <script type="text/jsx" src="js/footer.jsx"></script>
20     <script type="text/jsx" src="js/app.jsx"></script>
21 </body>
22 </html>

页面很干净,除了react基本js与其模板解析文件外,还多了一个director.js,因为react本身不提供路由功能,所以路由的工作便需要插件,director便是路由插件,这个不是我们今天学习的重点,然后是两个js文件:

 1 var app = app || {};
 2 
 3 (function () {
 4   'use strict';
 5 
 6   app.Utils = {
 7     uuid: function () {
 8       /*jshint bitwise:false */
 9       var i, random;
10       var uuid = '';
11 
12       for (i = 0; i < 32; i++) {
13         random = Math.random() * 16 | 0;
14         if (i === 8 || i === 12 || i === 16 || i === 20) {
15           uuid += '-';
16         }
17         uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random))
18                     .toString(16);
19       }
20 
21       return uuid;
22     },
23 
24     pluralize: function (count, word) {
25       return count === 1 ? word : word + 's';
26     },
27 
28     store: function (namespace, data) {
29       if (data) {
30         return localStorage.setItem(namespace, JSON.stringify(data));
31       }
32 
33       var store = localStorage.getItem(namespace);
34       return (store && JSON.parse(store)) || [];
35     },
36 
37     extend: function () {
38       var newObj = {};
39       for (var i = 0; i < arguments.length; i++) {
40         var obj = arguments[i];
41         for (var key in obj) {
42           if (obj.hasOwnProperty(key)) {
43             newObj[key] = obj[key];
44           }
45         }
46       }
47       return newObj;
48     }
49   };
50 })();
utils

相关文章:

  • 2021-06-04
  • 2022-01-11
  • 2021-06-13
  • 2022-12-23
  • 2022-02-25
猜你喜欢
  • 2021-11-16
  • 2021-07-04
  • 2021-06-18
  • 2021-08-19
  • 2022-12-23
  • 2022-12-23
  • 2021-04-28
相关资源
相似解决方案