React.js是Facebook开发的框架。
http://facebook.github.io/react/
官网上的描述是「A JavaScript library for building user interfaces」 React.js是用来构造UI的框架。不是一个framework,只是用来构造UI的library,提供MVC中View的机能。
采用了它进行开发的自然有Facebook本身,Instagram、Yahoo、Airbnb等等,是当今备受瞩目的library。
React.js的特征
为了了解React.js的特征,先与其他library或framework相比较。比如像Todo MVC这样对框架进行选型,以发送表单为例子进行实装试验。
jQuery
单纯jQuery的写法:
1
2
3
4
5
6
7
8
9
|
// 点了Submit之后
$('form').on('submit', functino() {
// 创建dom元素
var $li = $('<li>');
// ...
// 追加到list
$('ul').append($li);
});
|
可是,这样被DOM的结构所限制,要追加什么功能都非常麻烦,测试case也不好写。
Backbone.js
Backbone.js可以一定程度上解决类似问题。Backbone.js把管理数据的Model、与管理表现的View分开、然后把View分成各种组件来设计。比如下面这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
var FormView = Backbone.View.extend({
onSubmit: function() {
// 创建data后只要追加到model
this.collection.add(data)
}
});
var ListView = Backbone.View.extend({
initialize: function() {
// model更新后追加到list
this.collection.on('add', this.render);
}
});
|
对FormView或ListView来说、都属于UI的一类功能、同类型的东西都只要调用组件就可以实现。
Backbone.js把数据和组件分开管理以后就更容易管理,但View的更新还需要手动来写仍然非常麻烦,Model与View之间传递事件也变得复杂,代码量扩大的时候就会出现很多问题。(和Backbone.js类似的library比如Marionette和Chaplin、本质上问题是一样的)
Angular.js、Vue.js
Angular.js、Vue.js为代表的、所谓MVVM系library的特征是数据一旦发生改变就会自动更新显示。
1
2
3
4
5
6
7
8
9
|
<form ng-submit="onSubmit()">
<input type="text" ng-model="text">
</form>
<ul>
<li ng-repeat="item in list">
{{item.text}}
</li>
</ul>
|
1
2
3
4
5
|
// Controller
$scope.onSubmit = function() {
// data更新后自动就显示了
$scope.list.push(newItem);
};
|
这样记录了HTML中用到的数据、JavaScript就自动把数据更新上去显示了。但是,这种类型的library在规模变大的时候要管理状态是很难的。
React.js
jQuery也好Backbone.js、Angular.js也罢,上面说了都不适应管理大规模的代码。虽然严谨的设计仍然可以成就方便管理的应用。但是,就违背了开发这些框架的本意,代码变得臃肿,设计也不简单。
React.js是规模再大也能提供便利管理的library。相反的,开发高速的小型应用Backbone.js或者Vue.js就更为合适。
React.js的特征是尽量做到组件无状态,便利的对组件进行管理:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
var Form = React.createClass({
onSubmit: function() {
// 通知父级数据有更新
},
render: function() {
return <form onSubmit={this.onSubmit}>...</form>;
}
});
var List = React.createClass({
render: function() {
// 从父级拿到数据进行更新
return <ul>{this.props.list.map(...)</ul>;
}
});
|
各组件都从父级拿数据然后再去创建View。这一点很重要,组件自身不需要维持状态。通常根据从外部得到的输入进行输出或者测试的情况(大多数情况来自父级组件),管理和复用性都很高。
当然,全部组件都不维持状态的话与静态的HTML并无二致,有一些组件还是需要保持状态的。让尽量少的组件来维持状态,就是设计React.js的初衷。
通常是根节点组件在维持状态,它的子节点组件来传递:
然而,组件的状态一旦改变就会自动刷新树的结构,不需要手动操作,这与Angular.js之类是一样的。
数据一旦改变就自动显示的考虑也相同。
大部分根节点以外的组件都不用保持状态,用户的输入随着用户操作改变的话是需要维持自身的状态。
Virtual DOM
根节点组件保持了状态,它的状态一改变就刷新DOM树的设计非常单纯,一部分的改变导致整个DOM树的刷新影响性能。
这样React.js就引入了Virtual DOM的概念。
Virtual DOM粗略的说,就是构成DOM树的JavaScript对象、计算需要更新数据的对象后再实际更新最少需要更新的DOM元素。
React.js是用React.createClass
创建组件,用render方法的返回值得到Virtual DOM。Virtual DOM是React.createElement创建而
成的。
1
2
3
4
5
6
7
8
|
var MyComponent = React.createClass({
render: function() {
return React.createElement("div", {className: "foo"},
React.createElement("div", {className: "bar"},
"Hello ", this.props.name
)
);
});
|
而且,JSX使用独特的语法,用XML类似的语法来表现Virtual DOM。
1
2
3
4
5
6
7
8
9
10
11
|
var MyComponent = React.createClass({
render: function() {
return (
<div className="foo">
<div className="bar">
Hello {this.props.name}
</div>
</div>
);
}
});
|
用react-tools和babel这样的tool可以直接转换成JavaScript。
React.js的性能
有人觉得React.js速度很快,但真的是这样吗?采用了Virtual DOM以后,即便要更新根节点的数据也能最小限度的刷新DOM,确实应该是很快的,比刷新整个DOM树是要快很多。分三种情况进行性能测定:
- 用Backbone.js只刷新有变化部分的DOM
- 用Backbone.js当部分发生改变却刷新整个DOM树
- 用React.js当部分发生改变的时候用Virtual DOM计算好要改动的部分再更新DOM
结果如下图所示:
这样的话,React.js、Backbone.js只刷新部分DOM树明显是比刷新整个DOM树要快很多的。
如上所述,只让根节点保持状态是非常简单的设计,在这种简单设计的前提下同样提供比较好的性能,是React.js最大的特点。
Flux
Flux是Facebook非常提倡的一种应用架构设计。经常被拿来和MVC作比较、与MVC不同的是它的数据只有一个流向。
至此为止、作为React.js的设计理念、由根节点保持状态,一旦什么发生改变就刷新那个状态,不管怎样的变更都通知刷新,却不会知道具体是怎样的变更。
例如,DOM树的末端组件检测到用户的操作,需要刷新根节点的状态。这个时候,React.js中这个末端组件是无状态的,必须要立刻通过事件传递通知父级组件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
var Parent = React.createClass({
handleChange: function(changedData) {
// 子元素有什么需要更新的时候
},
render: function() {
// 设定子元素的事件处理函数
return <Child onChange={this.handleChange}>a</Child>;
}
});
var Child = React.createClass({
handleSubmit: function() {
// 父元素接收到事件进行处理
this.props.onChange(changedData);
},
render: function() {
// 设定表单的事件处理函数
return <form onSubmit={this.handleSubmit}>...</form>;
}
});
|
然而、用这种方法在嵌套较深的情况下更新组件时要传递给用来保持状态的根组件就非常的麻烦。那样,Flux中由View引起的变更进入Action(行为)然后通过Dispatcher(调度)之后Store(存储)后才能完成状态更新。
所谓Store是对数据的管理,相当于MVC模型中的M。View的根节点组件会读取Store中的变化,Store会自动更新数据。
Flux本身的设计理念是如此,实际应用却形形色色。Flux的原始设计是Facebook做的facebook/flux。是最小限度采用Dispacher的例子,其他实例还有如下一些:
总结
上文说明了React.js和Flux的设计思想和特征。React.js适用于有扩展需求的大型项目,却不适合敏捷开发小型应用。那样的需求采用Angular.js或者Vue.js的更多。静态的页面中少许UI单纯用jQuery也很便利。
实际上用过React.js和Flux写过应用就知道,组件要对应状态是一件很麻烦的事。然而,用这些麻烦的代价换来的是具有更强稳定性、更易维护的应用。
如果有这样的需求,还是值得考虑采用React.js的。