[心得] 如何提高 React App 的性能
台湾朋友盧承億投稿
这篇是我最近在研究React.js时的一些心得,关于怎么使用 shouldComponentUpdate 及 immutable.js 提高性能。
Virtual DOM
因为使用传统的方式对DOM 进行操作会很慢,为此React 设计了 Virtual DOM 这个中间层来降低操作 DOM 的成本。
Virtual DOM 是一个类似实际 DOM 节点的树状结构,当有数据改变时才会透过 diff 算法计算最小差异并更新到实际的 DOM 上。
所以当我们 render 时,其实都是 render 到 Virtual DOM 上,React 会自己帮我们算出最小差异然后更新。
shouldComponentUpdate
在讲 shouldComponentUpdate 之前,先来看看 Component 的生命周期。当 state 或是 props 改变的时候,会先呼叫 shouldComponentUpdate(),如果回传 true 才会 render 到Virtual DOM,最后再由React 的 diff 算法决定要怎么更新 Real DOM。
因为 shouldComponentUpdate() 预设是回传 true,也就是 “一律重绘”,所以不管有没有改变,一律重新 render到Virtual DOM 上,最后由 diff 算法判断到底哪些需要改变。
虽然 React 的 diff 算法很高效,但如果数量一多,React 要递归跑完所有component 的 render,还是会拖慢速度,所以可以自己实现shouldComponentUpdate,如果数据相同就不要重新 render。
定义一个命叫 Item的Component:
// <Item content="item1" />
var Item = React.createClass({
render(){
return <h1> {this.props.content} </h1>;
}
});
可以加入一个shouldComponentUpdate(nextProps, nextState):
shouldComponentUpdate(nextProps, nextState){
if(this.props.content !== nextProps.content) return true;
return false;
}
只有在this.props.content改变的时候才重新 render,这样就可以大大减少重新render 的次数。
可能会遇到什么问题?
可能会遇到props是对象或者数组的情况,但对象跟数组没办法比较相等,看看下面的例子:
var obj1 = {name: 'Larry', age: 19};
var obj2 = {name: 'Larry', age: 19};
console.log(obj1 === obj2);//false
console.log(obj1.name === obj2.name && obj1.age === obj2.age);//true
var arr1 = [1, 2, 3];
var arr2 = [1, 2, 3];
console.log(arr1 === arr2);//false
// 比較 array 內所有元素 -> true
console.log(function(){
if(arr1.length != arr2.length) return false;
for(var i=0 ; i<arr1.length ; i++){
if(arr1[i] !== arr2[i]) return false;
}
return true;
}());
用===只能比较是不是同一个对象,如果要比较内容的话要自己进行比较,数组也是要自己遍历整个数组,所以shouldComponentUpdate可能会写成这样:
// <List items={['item1', 'item2', 'item3']} />
shouldComponentUpdate(nextProps, nextState){
if(this.props.items.length != nextProps.items.length) return true;
for(var i=0 ; i<this.props.items.length ; i++){
if(this.props.items[i] !== nextProps.items[i]) return true;
}
return false;
}
但因为是遍历整个数组,如果数量一多的话还是会很慢,这时候就可以用immutable.js加速。
Immutable.js
immutable.js 是由Facebook 开源的一个 library,提供各种不同的结构,可以用npm install immutable安装。
比较常用的有 List 跟 Map,immutable.js 中的 List 就像是 Array
var Immutable = require('immutable');
var list1 = Immutable.List.of('a', 'b', 'c');
// ['a', 'b', 'c']
var list2 = Immutable.List.of('a', 'b', 'c');
// ['a', 'b', 'c']
console.log(Immutable.is(list1, list2));
// true
console.log(list1.get(0));
// 'a'
list1 = list1.set(0, 'b');
// list1 = ['b', 'b', 'c']
console.log(Immutable.is(list1, list2));
// false
immutable.js 中的 Map 就像是Object
var map1 = Immutable.Map({name: 'Larry', age: 19});
var map2 = Immutable.Map({name: 'Larry', age: 19});
console.log(Immutable.is(map1, map2));
// true
map1 = map1.set('name', 'Larry Lu');
// map1 = {name:'Larry Lu', age:19}
console.log(map1.get('name'));
// 'Larry Lu'
conosle.log(Immutable.is(map1, map2));
// false
这样就可以用Immutable.js来比较数组跟对象,而且Immutable.js不是把每个值都取出来比较,而是在创建 List 跟 Map 时就计算得到一个 hashvalue,比较时就比较那个 hashvalue,所以速度会快非常多。
还有一个要注意的点,immutable.js创造出来的对象是不可变的,在 js 内要更改数组内的元素只要使arr[index] = value,但用immutable.js时要使list = list.set(index,value)。因为set时不会更改原本的而是创造一个新的List,map也是一样,所以一定要使map = map.set('name','Larry Lu')。
结论
综合shouldComponentUpdate 及 Immutable.js 之后,最后就可以把 component 写成这样:
// <Item info={Immutable.Map({'name': 'Larry', age: 19})} />
var Item = React.createClass({
shouldComponentUpdate(nextProps, nextState){
return !Immutable.is(this.props.info, nextProps.info);
},
render(){
return (
<div>
<h1> {'name: ' + this.props.name} </h1>
<h2> {'age: ' + this.props.age} </h2>
</div>
);
}
});
这样就可以减少重新 render 的次数,而且在判断要不要重新 render 时也可以非常快速,让原本就很快的 React 变得更快。
【React启蒙系列文章】
三、[React启蒙系列] React和Babel的基本使用
【您可能感兴趣的文章】
前端圈--打造专业的前端技术会议
为web前端开发者提供技术分享和交流的平台
打造一个良好的前端圈生态,推动web标准化的发展
官网:http://fequan.com
微博:fequancom | QQ群:41378087
长按二维码关注我们
投稿:content@fequan.com
赞助合作:apply@fequan.com