筹备 - 知识点
1、 filber 树
function App() { return ( <div> i am <span>KaSong</span> </div> )}ReactDOM.render(<App />, document.getElementById("root"));
2、双缓存
- 在React中最多会同时存在两棵Fiber树。以后屏幕上显示内容对应的Fiber树称为current Fiber树,正在内存中构建的Fiber树称为workInProgress Fiber树。
- React利用的根节点通过current指针在不同Fiber树的rootFiber间切换来实现Fiber树的切换。
- 当workInProgress Fiber树构建实现交给Renderer渲染在页面上后,利用根节点的current指针指向workInProgress Fiber树,此时workInProgress Fiber树就变为current Fiber树。
- 每次状态更新都会产生新的workInProgress Fiber树,通过current与workInProgress的替换,实现DOM更新。
3、位运算
- React 源码中利用了大量的位运算判断
科普文档:https://juejin.cn/post/684490...
4、2个阶段
- Render (深度遍历)
beginWork
- Mount -> 生成fiber树
- Update -> 更新fiber树
effectTag
- 标示节点的操作类型(删除、更新、插入等)16位的2进制数
completeWork
- Mount 由子到父 创立dom(虚构) 并插入父级, 直到根节点rootFiber打上 Placement effectTag 一次行实现整个dom的插入
实现虚构dom的插入更新 workInPeogress.updateQueue寄存props
effectList
- 在completeWork 中将所有带有effectTag(能够了解为更新标识)的filber 节点放入 effect单向链表中
- Commit 阶段
- 不可中断
- 实现渲染工作- 执行副作用
useEffect
- 该 Hook 接管一个蕴含命令式、且可能有副作用代码的函数。
- 赋值给 useEffect 的函数会在组件渲染到屏幕之后执行。你能够把 effect 看作从 React 的纯函数式世界通往命令式世界的逃生通道。
- effect 将在每轮渲染完结后执行,但你能够抉择让它 在只有某些值扭转的时候 才执行。
effect 的执行机会
- 遗记官网这句话。。。理论利用中如果这样了解可能会带来一些问题。。。
useEffect
- 与 componentDidMount、componentDidUpdate 不同的是,在浏览器实现布局与绘制之后,传给 useEffect 的函数会提早调用。这使得它实用于许多常见的副作用场景,比方设置订阅和事件处理等状况,因而不应在函数中执行阻塞浏览器更新屏幕的操作。
useLayoutEffect
- 其函数签名与 useEffect 雷同,但它会在所有的 DOM 变更之后同步调用 effect。能够应用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 外部的更新打算将被同步刷新。
尽可能应用规范的 useEffect 以防止阻塞视觉更新。
https://codesandbox.io/s/opti...:395-425
https://codesandbox.io/s/hung...
effect return 的执行机制
useEffect return 的在什么期间执行呢?
- 组件在节点中隐没(卸载)
- useEffect 执行
https://codesandbox.io/s/opti...
useEffect 为什么依赖项,不填写 仍然能够拿到最新的state?
- 无论依赖项是否变动 每次从新push create函数(咱们useEffect中的callback)
function updateEffectImpl(fiberEffectTag, hookEffectTag, create, deps) { var hook = updateWorkInProgressHook(); var nextDeps = deps === undefined ? null : deps; var destroy = undefined; if (currentHook !== null) { // 获取以后effect节点所在队列地位 var prevEffect = currentHook.memoizedState; destroy = prevEffect.destroy; if (nextDeps !== null) { var prevDeps = prevEffect.deps; // 判断前后的 deps 是否雷同 if (areHookInputsEqual(nextDeps, prevDeps)) { //创立一个新的 Effect,并把它增加到更新队列 //tag标记NoEffect$1 = 0 pushEffect(NoEffect$1, create, destroy, nextDeps); return; } } } // 如果状态发生变化,则将以后effect的tag设置UnmountPassive | MountPassive,并后续在commitHookEffectList触发更新 sideEffectTag |= fiberEffectTag; hook.memoizedState = pushEffect(hookEffectTag, create, destroy, nextDeps);}
- 能够看到updateLayoutEffect 的实现和 updateEffect 的实现一样。只是传入的tag不同。 fiberEffectTag和hookEffectTag会在commit阶段做比照,决定effect的执行机会。
源码断点剖析
import { React, useCallback, useState, useEffect } from "../../CONST";import BatchCount from "./BatchCount";const asyncTime = new Promise((resolve, reject) => { resolve();});export default function BatchState() { const [count1, setCount1] = useState(0); const [count2, setCount2] = useState(0); const handleClick = useCallback(() => { asyncTime.then(() => { setCount1((count1) => { console.log("+1 = ", count1); return count1 + 1; }); setCount1((count1) => { console.log("+2 = ", count1); return count1 + 2; }); }); // setCount1((count1) => { // console.log("+1 = ", count1); // return count1 + 1; // }); // setCount1((count1) => { // console.log("+2 = ", count1); // return count1 + 2; // }); }, [count1, count2]); useEffect(() => { console.log("dep null"); }); useEffect(() => { console.log("effect []]"); return () => { console.log("return []") } }, []); useEffect(() => { console.log("effect1"); return () => { console.log("return effect1") } }, [count1]); useEffect(() => { console.log("effect2"); return () => { console.log("return effect2") } }, [count1]); // console.log('render', {count1, count2}) return ( <div> <button onClick={handleClick}>batch add</button> <BatchCount count1={count1} count2={count2} /> </div> );}
- push
- componentUpdateQueue 寄存所有 effect hooks 链表
function pushEffect(tag, create, destroy, deps) { const effect: Effect = { tag, create, destroy, deps, // Circular next: (null: any), }; let componentUpdateQueue: null | FunctionComponentUpdateQueue = (currentlyRenderingFiber.updateQueue: any); if (componentUpdateQueue === null) { componentUpdateQueue = createFunctionComponentUpdateQueue(); currentlyRenderingFiber.updateQueue = (componentUpdateQueue: any); componentUpdateQueue.lastEffect = effect.next = effect; } else { const lastEffect = componentUpdateQueue.lastEffect; if (lastEffect === null) { componentUpdateQueue.lastEffect = effect.next = effect; } else { const firstEffect = lastEffect.next; lastEffect.next = effect; effect.next = firstEffect; componentUpdateQueue.lastEffect = effect; } } return effect;}
- mount时候
function mountEffectImpl(fiberEffectTag, hookEffectTag, create, deps): void { const hook = mountWorkInProgressHook(); const nextDeps = deps === undefined ? null : deps; currentlyRenderingFiber.effectTag |= fiberEffectTag; hook.memoizedState = pushEffect( HookHasEffect | hookEffectTag, create, undefined, nextDeps, );}
- Update
- currentHook 之前的side effect hooks
function updateEffectImpl(fiberEffectTag, hookEffectTag, create, deps): void { const hook = updateWorkInProgressHook(); const nextDeps = deps === undefined ? null : deps; let destroy = undefined; if (currentHook !== null) { const prevEffect = currentHook.memoizedState; destroy = prevEffect.destroy; if (nextDeps !== null) { const prevDeps = prevEffect.deps; if (areHookInputsEqual(nextDeps, prevDeps)) { pushEffect(hookEffectTag, create, destroy, nextDeps); return; } } }
HookLayout | HookHasEffect // 2 | 1 = 3
- 执行阶段
function commitHookEffectList( unmountTag: number, mountTag: number, finishedWork: Fiber,) { const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any); let lastEffect = updateQueue !== null ? updateQueue.lastEffect : null; if (lastEffect !== null) { const firstEffect = lastEffect.next; let effect = firstEffect; do { if ((effect.tag & unmountTag) !== NoHookEffect) { // Unmount const destroy = effect.destroy; effect.destroy = undefined; if (destroy !== undefined) { destroy(); } } if ((effect.tag & mountTag) !== NoHookEffect) { // Mount const create = effect.create; effect.destroy = create(); } effect = effect.next; } while (effect !== firstEffect); }}
这里要说的是 _useEffect_ 会进入异步调度流程
在commitRoot有这样的一个判断
if (firstEffect !== null && rootWithPendingPassiveEffects !== null) { //这个commit蕴含passive effect,他们不须要执行直到下一次绘制之后,调度一个回调函数在一个异步事件中执行他们 var callback = commitPassiveEffects.bind(null, root, firstEffect); if (enableSchedulerTracing) { callback = unstable_wrap(callback); } passiveEffectCallbackHandle = unstable_runWithPriority(unstable_NormalPriority, function () { return schedulePassiveEffects(callback); }); passiveEffectCallback = callback; }
参考
https://juejin.cn/post/684490...
https://react.iamkasong.com/h...
https://xiaoxiaosaohuo.github...
https://devrsi0n.com/articles...