为什么说React的响应粒度是组件级别的?

9 阅读3分钟

著有《React18 设计原理》《javascript地月星》等多个专栏。 欢迎关注。

创作不易,有帮助别忘了点赞,收藏,评论 ~ 你的鼓励是我继续挖干货的动力。

本文全部都是原创内容,商业转载请联系作者获得授权,非商业转载需注明出处,感谢理解 ~

推荐指数(值得一读):⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️

原文 👉 juejin.cn/post/763192…

React 的响应粒度是组件级别的

这个概念表达的是,节点是否有变化、选择 bailout 还是 reconcile 的判断方式——— lanes 和 props,这个判断方式是组件级别的。

function MyComp(){
    let [xx,setXX] = useState();
    return (<div onClick=setXX>
               <div>
                    <p>{xx}</<p>
               </div>
            </div>)
}

父组件中:
<div>
  <MyComp {父组件传入的属性没有发生任何变化} />
</div>

组件useState发生了变化,

首先,遍历组件Fiber,oldProps===newProps,这个组件的父组件没有变化,所以组件的 props 没有变化。但是组件useState有变化,所以组件的 lanes 被标记。renderWithHooks得到新的react ele。组件和内部元素的react ele变成新的对象。

接着,遍历组件内的元素Fiber,使用 prors 判断,这时 props 的值是 react ele,于是 oldProps!==newProps,整个组件都需要reconcile。———没有做到精准感知元素Fiber的lanes,而是使用整个组件的react ele。这样子,只是p使用了state,其实只要协调p就好了,但实际上包括div全部都reconcile了。不会bailout div,然后reconcile p,而是整个组件所有的元素都reconcile。

这个过程中, react ele 是以组件为单位生成的,只有组件类型的 Fiber 会执行 renderWithHooks(整个应用的 jsx->react ele 不是一次性生成的。而是按组件生成,遍历到组件 Fiber 的时候生成组件的 ele)。至于 lanes,只有组件 lanes 会有效。

产生的问题:1. 如果一个很庞大的组件(元素数量很多,层级很深),它只有 1 个状态发生了简单的变化,并且只有 1 个子元素中简单的使用了,但却会导致整个组件,包括子组件全部重新渲染,即使子组件没有变化。2. 如果子组件也有变化,但是等级不高,按设计意图,不同等级 lanes 应该分批处理,但父组件的更新把子组件一起更新了,违背了分级处理的设计意图。例如父组件 lanes =1,子组件 lanes=64。

这两种情况都会有性能问题。对于 1,应该把组件变小,或者把这个状态分离到独立子组件。对于 2,应该给子组件缓存。

状态的声明和状态的使用最大的问题:只有组件有 state。元素 Fiber只能没有自己的 state。元素Fiber只能使用组件 Fiber 的 state。

观察 MyComp 的例子,一个最大的现象:p 使用的实际是 MyComp 的 state,而不是自己的 state。这个 state 是归 MyComp 管的!

  1. 组件 Fiber,能在自己函数“体内”写useState,lanes 能标记。
  2. 元素 Fiber,没有自己的函数声明去写 useState;元素使用的是组件的 state;这不算作自己的 lanes。例如 p没有自己的“身体”写useState。p 使用的是组件的状态。

所以,虽然 lanes 是标记节点有更新,但实际上元素的 lanes 一直是 0。 (React 有时候会直接把组件的 lanes 同步给组件内元素的 lanes,但实际上元素 lanes 没什么用处)

useState是组件级别的,这就是为什么 lanes 一定是组件级别的。

props 、 react ele 和 lanes 都来自 state

  1. 点击-->组件 state --> lanes!=0
  2. 点击-->组件 state-->如果子组件使用了 props,props 就是 state。,子组件 props 就是父组件 state。父、子组件协调。
  3. 点击-->组件 state-->lanes!=0-->react ele 重新生成。整个组件协调。
  4. 另一种变化来源是上下文 context-provider

可以认为,它们都是组件级别的。