render阶段作用是什么?
render的作用是根据一次更新中产生的新状态值,通过 React.createElement ,替换成新的状态,得到新的 React element 对象,新的 element 对象上,保存了最新状态值。 createElement 会产生一个全新的props。
React 会调和由 render 函数产生 chidlren,将子代 element 变成 fiber(这个过程如果存在 alternate,会复用 alternate 进行克隆,如果没有 alternate ,那么将创建一个),将 props 变成 pendingProps ,至此当前组件更新完毕。然后如果 children 是组件,会继续重复上一步,直到全部 fiber 调和完毕。
说到对render 的控制,究其本质,主要有以下两种方式:
- 第一种就是从父组件直接隔断子组件的渲染,经典的就是 React.memo,缓存 element 对象。
- 第二种就是组件从自身来控制是否 render ,比如:类组件的PureComponent ,shouldComponentUpdate。函数组件的useMemo,useCallback
React.Memo
React.memo 可作为一种容器化的控制渲染方案,可以对比 props 变化,来决定是否渲染组件.
React.memo 接受两个参数,第一个参数 Component 原始组件本身,第二个参数 compare 是一个函数,可以根据一次更新中 props 是否相同决定原始组件是否重新渲染。
memo的几个特点是:
- React.memo: 第二个参数 返回 true 组件不渲染 , 返回 false 组件重新渲染。和 shouldComponentUpdate 相反,shouldComponentUpdate : 返回 true 组件渲染 , 返回 false 组件不渲染。
- memo 当二个参数 compare 不存在时,会用浅比较原则处理 props ,相当于仅比较 props 版本的 pureComponent 。
- memo 同样适合类组件和函数组件。
- 被 memo 包裹的组件,element 会被打成
REACT_MEMO_TYPE类型的 element 标签,在 element 变成 fiber 的时候, fiber 会被标记成 MemoComponent 的类型
pureComponent
纯组件是一种发自组件本身的渲染优化策略,当开发类组件选择了继承 PureComponent ,就意味这要遵循其渲染规则。规则就是浅比较 state 和 props 是否相等。
PureComponent 可以让组件自发的做一层性能上的调优,但是,父组件给是 PureComponent 的子组件绑定事件要格外小心,避免两种情况发生:
- 避免使用箭头函数。不要给是 PureComponent 子组件绑定箭头函数,因为父组件每一次 render ,如果是箭头函数绑定的话,都会重新生成一个新的箭头函数, PureComponent 对比新老 props 时候,因为是新的函数,所以会判断不想等,而让组件直接渲染,PureComponent 作用终会失效。
- PureComponent 的父组件是函数组件的情况,绑定函数要用 useCallback 或者 useMemo 处理。这种情况还是很容易发生的,就是在用 class + function 组件开发项目的时候,如果父组件是函数,子组件是 PureComponent ,那么绑定函数要小心,因为函数组件每一次执行,如果不处理,还会声明一个新的函数,所以 PureComponent 对比同样会失效,
PureComponent 原理及其浅比较原则
pureComponentPrototype 纯组件构造函数的 prototype 对象,绑定isPureReactComponent 属性
isPureReactComponent 这个属性在更新组件 updateClassInstance 方法中使用的,在生命周期章节中已经讲过,相信看过的同学都会有印象,这个函数在更新组件的时候被调用,在这个函数内部,有一个专门负责检查是否更新的函数 checkShouldComponentUpdate
function checkShouldComponentUpdate(){
if (typeof instance.shouldComponentUpdate === 'function') {
return instance.shouldComponentUpdate(newProps,newState,nextContext) /* shouldComponentUpdate 逻辑 */
}
if (ctor.prototype && ctor.prototype.isPureReactComponent) {
return !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
}
}
- isPureReactComponent 就是判断当前组件是不是纯组件的,如果是 PureComponent 会浅比较 props 和 state 是否相等。
- 还有一点值得注意的就是 shouldComponentUpdate 的权重,会大于 PureComponent。
- shallowEqual 是如何浅比较的呢
shallowEqual 浅比较流程:
- 第一步,首先会直接比较新老 props 或者新老 state 是否相等。如果相等那么不更新组件。
- 第二步,判断新老 state 或者 props ,有不是对象或者为 null 的,那么直接返回 false ,更新组件。
- 第三步,通过 Object.keys 将新老 props 或者新老 state 的属性名 key 变成数组,判断数组的长度是否相等,如果不相等,证明有属性增加或者减少,那么更新组件。
- 第四步,遍历老 props 或者老 state ,判断对应的新 props 或新 state ,有没有与之对应并且相等的(这个相等是浅比较),如果有一个不对应或者不相等,那么直接返回 false ,更新组件。
打破渲染限制
- 1 forceUpdate。类组件更新如果调用的是 forceUpdate 而不是 setState ,会跳过 PureComponent 的浅比较和 shouldComponentUpdate 自定义比较。其原理是组件中调用 forceUpdate 时候,全局会开启一个 hasForceUpdate 的开关。当组件更新的时候,检查这个开关是否打开,如果打开,就直接跳过 shouldUpdate 。
- 2 context穿透,上述的几种方式,都不能本质上阻断 context 改变,而带来的渲染穿透,所以开发者在使用 Context 要格外小心,既然选择了消费 context ,就要承担 context 改变,带来的更新作用。