优化 - 渲染控制 - 图1

渲染控制流程图

优化 - 渲染控制 - 图2

知识点


问:**render **阶段作用详解?
答:在组件一次更新中,类组件执行 render,函数组件执行renderWithHooksrenderWithHooks中执行函数组件本身),他们的作用是什么?真实渲染 DOM 了吗?真实 DOM 是在 commit 阶段挂载的。
**render**函数作用,根据一次更新产生新的状态值,通过 React.createElement,替换成新的状态,得到新的 React element 对象,新的 element 对象上保存了新的状态值。createElement会产生一个全新的 props。到此 render 函数完成任务。
接着 React 会调和由 render 函数产生的 children,将子代 element 对象变为 fiber(此过程若存在 alternate,将复用 alternate 进行克隆,若不存在,创建 alternate),将 props 变为 pendingProps,至此当前组件更新完成。然后,若 children 是组件,重复上步骤,直到调和完成。完成 render 阶段。


问:什么是**alternate**TODO???


问:**useMemo**详解?
语法:const cacheSomething = React.useMemo( create, deps )

  • create:函数,函数的返回值作为缓存值,React element 对象也可作为缓存值(如:useMemo(() => <xxxComponent/>, deps))。
  • deps:依赖项,若依赖项变化,缓存值更新;
  • cacheSomething:返回值,执行create函数后的返回值,若deps改变,返回重新执行的create函数的值,否则取上一次缓存的值。

useMemo原理 - TODO:

  • 联系:函数组件对应的 fiber 对象,fiber 对象是依组件级别创建的???

useMemo应用场景:

  • 可缓存 element 对象,从而达到条件渲染组件,优化性能;
  • 可缓存一些不是每次 render 都需要计算的值;
  • 可将属性、方法缓存起来,作为 PureComponent的绑定方法,或配合其他 Hooks 一起使用。

问:为什么将 Element 对象缓存起来,就实现了控制子组件不必要的渲染?
答:每次执行render本质上createElement会产生一个新的props,新的props会作为对应 fiber 的pendingProps,在此 fiber 更新调和阶段,React 对比 fiber 上 老oldProps和 新newProps(pendingProps)是否相等,若相等函数组件放弃子组件的调和更新,子组件不会重新渲染;若将 Element 对象缓存起来,props也就和 fiber 上oldProps指向相同的内存空间,也就相等,从而跳过本次更新。


问:**shallowEqual**过程?

  1. 比较新旧props或新旧state是否相等,若相等不更新组件;
  2. 判断新旧state或新旧props有不是对象或为null的,则直接返回 false,更新组件;
  3. 通过Object.keys获取新旧state或新旧props属性名 key变成数组,查看数组长度是否相等,相等,不更新组件,不相等,新增或删除,更新组件;
  4. 遍历旧state或旧props判断对应新state或新props,有没有与之对应并且相等(相等是浅比较),若有一个不对应、不相等,返回false,更新组件。

以上就是PureComponent做渲染节点优化的过程。


问:**useCallback****useMemo**区别?
答:useCallback接收两个参数,第一个参数就是需要缓存的函数,第二个参数是依赖项depsdeps改变返回新的函数。可用useCallback来缓存 element 对象。
useMemo接收两个参数,第一个参数是一个函数,需要执行这个函数,函数的返回值为缓存的内容,比起useCallbackuseMemo更像是缓存了一段逻辑,或说这段逻辑执行获取到的结果。


问:memo 主要逻辑?
答:

  • memo的第二个参数,判断是否执行更新,若无第二个参数,以浅比较 props 为diff规则。若相等,当前 fiber 完成工作,停止向下调和节点,所以被包含的组件不更新。
  • memo可理解为 HOC,它的阻断更新流程的机制,是通过控制下一级 children ,也就是 memo包裹的组件,是否继续调和渲染,来达到目的。
  • 禁用const newIndex = React.memo(() => true) 来阻断渲染。

精选评论

渲染控制的常见情形有:包含图片等资源的列表。
加 key,避免 prop 的不必要更新,
特别注意的是列表项含有用户交互事件,传给 onClick 事件等的 prop,不能直接使用 (item) => { },不然每次列表项都是新的 prop,不断触发列表项渲染,几十项的图片等资源,很耗性能。
可以用 this.method 或 useCallback,根据 index, 在函数里再去取对应 item。
渲染劫持方案: