本文正在参与「金石方案 . 瓜分6万现金大奖」
虚拟DOM 很好,但别贪杯。
React对页面更新都是对组件的从头烘托,将之前烘托的内容悉数再烘托一次。而是运用虚拟DOM来到达最小化修正实在DOM,到达高效的更新。
可是,有时清晰知道烘托成果不会有改动,那么这时对虚拟DOM的核算便是“一次糟蹋”。
所以假如能够在开端核算虚拟DOM之前就能够判别,那样能够干脆不要进行虚拟DOM核算和比较,速度就会更快。
留意,这儿说的糟蹋是核算Virtual DOM的糟蹋,并不是访问DOM树的糟蹋。
单个 React 组件的功能优化
shouldComponentUpdate
作为一个生命周期函数,shouldComponentUpdate
函数则决定“什么时分组件不需求从头烘托”。
这个函数默许回来是true,也便是默许每次更新的时分都要调用一切的生命周期函数。包括调用render函数,依据render函数的回来成果核算虚拟DOM。
当回来false时,就不会进行组件的更新。咱们需求依据本身考虑自定义改写回来false的状况。
<App style={{color:'red'}} onClick={() => sayHello()} />
shouldComponentUpdate
函数实现,每一次烘托都会以为style这个prop发生了改动,由于每次都会发生一个新的目标给style,在“浅层比较”中,类似==
先判别目标的指针,这儿创建了新目标{color:'red'}
,即使里边内容相同、也会以为是传入了一个新目标,即新props。那么App组件会从头烘托。
这儿赋值给onClick的是一个匿名的函数,而且是在赋值的时分发生的。也便是说,每次烘托App组件的时分,都会发生一个新的函数,也会造成App组件会从头烘托。
假如需求做“深层比较”,那便是某个特定组件的行为,需求开发者自己依据组件状况去编写。不过也要谨记,不要简略地递归比较一切层次的字段,由于传递进来的prop目标什么结构是无法意料的。
这儿官网有具体实现方法
shouldComponentUpdate 的效果
React.PureComponent 、 React.memo做“浅层比较”
大部分状况下,能够运用React.PureComponent
来代替手写shouldComponentUpdate
但它只进行浅比较,所以当 props 或者 state 某种程度是可变的话,浅比较会有遗漏,那你就不能运用它了。
例如当state
是一个目标时,传入给子组件,即使state
内部特点值改动。子组件也依然会以为props并未改动而不会更新。这也便是为什么React引荐运用不可变数据
。
这儿引用官网的例子
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() {
// 这部分代码很糟,而且还有 bug
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>
);
}
}
处理方法有许多,可是核心一点便是,不要直接修正本来的state而是要仿制一个新的state在新的state的基础上进行改善。
官网的处理方案
多个React组件的功能优化
多个React组件之间组合的烘托进程。和单个React组件的生命周期相同,React组件也要考虑三个阶段:装载阶段、更新阶段和卸载阶段。
装载阶段和卸载阶段都是组件必须阅历一次的,这无可防止,也没有什么太多能够考虑的点。
那么咱们主要看更新阶段。
React的谐和(Reconciliation)进程
在装载进程中,React经过render方法在内存中发生了一个树形的结构,树上每一个节点代表一个React组件或者原生的DOM元素,这个树形结构便是所谓的Virtual DOM。React依据这个Virtual DOM来烘托发生浏览器中的DOM树。
Reconciliation(谐和)便是React找寻新旧虚拟DOM的不同的进程。
依照核算机科学目前的算法研究成果,比照两个N个节点的树形结构的算法,时间复杂度是O(N3),可是为了下降算法时间复杂度,React做出了部分舍弃。
1.节点类型不同的状况
假如树形结构根节点类型不相同,从头构建新的DOM树,原有的树形上的React组件会阅历“卸载”的生命周期,即使或许这颗树的某些节点能够复用,为了防止O(N3)的时间复杂度,React必须要挑选一个更简略更方便的算法,也就只能采用这种方法。
这儿旧组件会阅历卸载进程,新组件会阅历装载进程。
2.节点类型相同的状况
假如两个树形结构的根节点类型相同,React就以为本来的根节点只需求更新进程,不会将其卸载,也不会引发根节点的从头装载。
依据组件类型进行不同处理
- DOM元素组件
对于DOM元素类型,React会保留节点对应的DOM元素,只对树形结构根节点上的特点和内容做一下比对,然后只更新修正的部分。
- React组件
React能做的仅仅依据新节点的props去更新本来根节点的组件实例,引发这个组件实例的更新进程。
在处理完根节点的比照之后,React的算法会对根节点的每个子节点重复相同的动作,这时分每个子节点就成为它所覆盖部分的根节点,处理方法和它的父节点完全相同。
3.多个子组件的状况
找出两个子组件序列的不同之处,现有的核算出两个序列差异的算法时间是O(N2)。相同为了下降时间复杂度,React挑选了不是寻觅两个序列的精确不同,而是直接挨个比较每个子组件。
当新增加子组件不是在末尾插入时,子组件之间的次序被打乱了,就会造成旧的子组件不能被复用的境况(只能触发旧组件的更新)。
这儿React又推出了一个Key关键特点来处理这个问题
key
React官网的key
key特点就像身份证相同,每个组件都能够具有一个独一无二的key特点用于标识。
当新增加子组件不是在末尾插入时,子组件之间的次序被打乱了,可是咱们有key特点,所以咱们就能够靠key特点来尽或许复用旧子组件(防止旧组件的更新)。
留意:
key值虽然能够在每个时间都唯一,可是变来变去,那么就会误导React做出过错判别,甚至导致过错的烘托成果。也便是要保证key的值在组件的装载->更新->卸载的进程都得保证共同。
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs <li key={index}> {todo.text}
</li>
);
假如列表项目的次序或许会改动,咱们不主张运用索引来用作 key 值,由于这样做会导致功能变差,还或许引起组件状况的问题。能够看看 Robin Pokorny 的深度解析运用索引作为 key 的负面影响这一篇文章。翻译版
假如你挑选不指定显式的 key 值,那么 React 将默许运用索引用作为列表项目的 key 值。
参考
《深入浅出React和Redux》
《React官网》