通常,React 通常会在渲染到视图中去的时候,会使用几种特殊的技术最小化要更新的DOM 节点从而实现性能优化,尽管在大部分的应用中,React 为了更快的引导用户使用,而没有做太多的优化工作,然而这些技术的使用性是必要的。
1.尽量避免更新
在React 建立和将内部的数据呈现给视图的过程中,React 做了如下的事情:通过在真实DOM 和React 应用之间建立一个过渡层,这个过渡层有时被称为”虚拟DOM“。虽然不是DOM ,但是有着和DOM 一样的机制处理,几乎所有的操作都会先在虚拟DOM 上进行处理,最后才更新到DOM 节点上的
当组件的属性或者状态发生改变时,React 会比较旧的DOM 和新的DOM 是不是不同,来确定更新当前DOM 节点的必要性,如果不同的话,React 就会更新这个DOM 节点
在我们写代码的过程中我们可以通过一个函数去控制当前的组件是否需要进行更新,什么时候一定要更新,什么时候不更新。
这个函数叫做shouldComponentUpdate 生命周期更新函数,这个函数会发生在重新render 视图渲染之前,此函数在React 中默认是返回true ,然后会执行render 函数进行更新。
shouldComponentUpdate(nextProps,nextState) {
return true;
}
如果你已经确定了在某一个种情况视图不需要更新,就可以返回false ,这会跳过render 函数和它的子组件的render 函数
2.shouldComponentUpdate 的更新行为
在下面这张图中,SCU 表示shouldComponentUpdate 的return 情况,vDOMEq 表示要更新到视图中的React 元素是否相等
在C2 之前shouldComponentUpdate 返回了false ,所以C2 包括C4 ,C5 都不会进行更新
对于C1 ,C3 ,shouldComponentUpdate 返回了true ,所以React 会递归到更深的地方去检查他们是否需要更新,C6 时shouldComponentUpdate 返回了true ,并且旧元素和新元素不同即vDOMEq 为红色,所以一定会更新DOM
最后的C8 则是vDOMEq 为绿色,旧元素和新元素没有变化,所以不会更新
整颗树下来,React 更新的节点其实静仅仅是C6 ,C3 之所有也更新都是因为内部元素C6 要更新,所以实际上我们需要更新的数据要少的很多,性能也就非常必要了
即使我们进行性能优化是非常有必要的,然而我们为了单单减少更新次数而增加我们的代码量是得不偿失的,所以React 由专门提供一个父类来完成这件事情。
React.PureComponent
class CounterButton extends React.PureComponent {
constructor(props) {
super(props);
this.state = {count: 1};
}
render() {
return (
<button
color={this.props.color}
onClick={() => this.setState(state => ({count: state.count + 1}))}>
Count: {this.state.count}
</button>
);
}
}
上面的代码就是自动进行优化了的。 当然这个父类有一点的缺陷,那就是它是引用比较,就是所谓的浅比较,对于对象和数组这些引用型数据就会出问题
class ListOfWords extends React.PureComponent {
render() {
return <div>{this.props.words.join(',')}</div>;
}
}
class WordAdder extends React.Component {
constructor(props) {
super(props);
this.state = {
words: ['marklar']
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
const words = this.state.words;
words.push('marklar');
this.setState({words: words});
}
render() {
return (
<div>
<button onClick={this.handleClick} />
<ListOfWords words={this.state.words} />
</div>
);
}
}
上面的功能是想一个数组中增加数据,而我们的底层组件ListOfWords 可以实时更新,然而在这份代码中是不起作用的,因为数组是引用型数据,我们比较数组== 号时,是相等的,所以不会进行更新,某种情况下,我们依旧需要直接重写shouldComponentUpdate 组件。
3.使用函数式编程规范
对于数据操作,请使用函数式编程规范,不影响原数据的值。如下
handleClick() {
this.setState(prevState => ({
words: prevState.words.concat(['marklar'])
}));
}
上面这份代码中没有改变prevState 的值,不要试图用push 来处理,现在ES6 可以更好的支持函数式编程,比如说之前说到过的扩展运算符... ,也可以用在上面那个功能里用
handleClick() {
this.setState(prevState => ({
words: [...prevState.words,'marklar'],}));
};
两份代码效果是一样的。
比如说你可以直接复制改变某一个对象的值
function updateColorMap(colormap) {
colormap.right = 'blue';
}
上述代码不如用如下代码:
function updateColorMap(colormap) {
return Object.assign({},colormap,{right: 'blue'});
}
Object.assign(ES6) 函数的用处是将对象colormap 中可以被for in 枚举的属性复制到{} 中并覆盖right 属性值为'blue' 。
这里也可以用扩展运算符
function updateColorMap(colormap) {
return {...colormap,right: 'blue'};
}
这个运算进行的是硬复制
4.尽可能的保持变量不变
即为常量,这里也是综合函数式编程的,将参数看成一个常量,我们唯有构造一个新的变量来处理,在我们之前大量的代码中反复用到的const 就是用于定义常量的,这是为了数据的安全性,和有效的控制数据变化
同时我们要尽可能让变量之间的共享性变小,比如说
const x = { foo: "bar" };
const y = x;
y.foo = "baz";
x === y;
上述代码中x 和y 共用了内存,进行了分享,隔离变量共享是为了有利我们的shouldComponentUpdate 函数处理,当我们用PureComponent 进行性能优化时,我们要消除引用型数据的干扰,进行变量共享分离是必要的,有一些插件可以非常快捷的实现这些功能,比如:Immutable.js
下一篇将讲React 不用ES6 语法实现
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|