旧的流程
编译阶段
jsx通过babel编译成render function的形式,render function的返回值是vdom
渲染阶段(vdom的渲染)
- 初次渲染:不需要diff,直接通过 dom api 增删改 dom
- 再次渲染:需要和上一轮的vdom进行diff算法,对比出可以复用的节点,边对比边通过 dom api 增删改 dom
存在的问题
- react 的 setState 会渲染整个 vdom,而一个应用的所有 vdom 可能是很庞大的,计算量就会很大,而js的计算时间太长会阻塞渲染,动画卡顿
fiber架构
编译阶段
jsx编译成render function(vdom)
渲染阶段
render阶段(reconcile + schdule)
-
reconcile调和
-
diff阶段发生在vdom 转换成 fiber数据结构(这种数据结构是单链表的形式)的过程中
-
diff算法找到vdom中变化的部分,然后打个effectTag的增删改查的标记,并且把有effectTag标记的节点收集到effectList队列里面
-
-
schdule
- reconcile 是可以打断的,由 schedule 调度
// 以前的递归渲染改成循环,方便打断 // 每次循环做一个fiber 的 reconcile,当前处理的fiber会放在workInProgress这个全局变量上 // 当循环完了,也就是 wip 为空了,就去执行commit阶段 function workLoop() { **// shouldYiled 方法就是判断待处理的任务队列有没有优先级更高的任务,有的话就先处理那边的 fiber,这边的先暂停一下。** while (wip && shouldYield()) { performUnitOfWork(); } if (!wip && wipRoot) { commitRoot(); } }// 根据fiber类型做不同的处理 // 当前fiber处理完后,久把wip指向下一个fiber function performUnitOfWork() { const { tag } = wip; switch (tag) { case HostComponent: updateHostComponent(wip); break; case FunctionComponent: updateFunctionComponent(wip); break; case ClassComponent: updateClassComponent(wip); break; case Fragment: updateFragmentComponent(wip); break; case HostText: updateHostTextComponent(wip); break; default: break; } if (wip.child) { wip = wip.child; return; } let next = wip; while (next) { if (next.sibling) { wip = next.sibling; return; } next = next.return; } wip = null; }// 函数组件和类组件的处理,就是调用render拿到vdom,然后继续处理渲染出的vdom function updateClassComponent(wip) { const { type, props } = wip; const instance = new type(props); const children = instance.render(); reconcileChildren(wip, children); } function updateFunctionComponent(wip) { renderWithHooks(wip); const { type, props } = wip; const children = type(props); reconcileChildren(wip, children); }
commit阶段
- before mutation:在 dom 操作之前,会异步调度 useEffect 的回调函数
- mutation: 遍历effectList队列,根据 effectTag 来增删改 真实dom
- layout:同步调用 useLayoutEffect 的回调函数。而且这个阶段可以拿到新的 dom 节点,还会更新下 ref。
解决的问题
-
js阻塞渲染的问题
- 打断计算,分多次进行,由于按照以前边diff边操作dom无法打断,所以改成计算完了再一次性更新
-
那怎么打断计算
- 不再使用递归,改用循环,循环可以方便地打断恢复
- 并且把vdom数据结构换成fiber数据结构,fiber数据结构中存储着child,sibling,return(父节点)的信息,方便打断后还能找到其他的兄弟节点
react版本的重要更新
16还有其他的重要更新,这边先忽略
-
React16:
- 新的调度方式,Fiber
- hook
-
react17:
- lanes模型
-
react18:
-
root api的变更
之前是ReactDOM.render(, container);
后来是ReactDOM.createRoot(container).render();(并发渲染)
-
startTransition API(用于非紧急状态更新)
-
渲染的自动批处理 Automatic batching 优化
-
SSR 架构(Server-Side Rendering )
-