一、为什么采用异步调度?
v15 版本的 React由于对于大型的 React 应用,会存在一次更新,递归遍历大量的虚拟 DOM ,造成占用 js 线程,使得浏览器没有时间去做一些动画效果,伴随项目越来越大,项目会越来越卡。
二、Fiber root 和 root fiber 有什么区别?
fiberRoot:首次构建应用, 创建一个 fiberRoot ,作为整个 React 应用的根基。rootFiber: 如下通过 ReactDOM.render 渲染出来的,如上 Index 可以作为一个 rootFiber。一个 React 应用可以有多 ReactDOM.render 创建的 rootFiber ,但是只能有一个 fiberRoot(应用根节点)。
三、Fiber更新机制
初始化,
创建Fiber root 和root fiber,并在挂载的时候将而这关联起来,
然后开始到正式渲染阶段,会进入 beginwork 流程,
在新创建的 alternates 上,完成整个 fiber 树的遍历,包括 fiber 的创建。
最后会以 workInProgress 作为最新的渲染树,fiberRoot 的 current 指针指向 workInProgress 使其变为 current Fiber 树。到此完成初始化流程。
-
React Hooks 如何把状态保存起来?保存的信息存在了哪里?
函数组件对应 fiber 用 memoizedState 保存 hooks 信息,每一个 hooks 执行都会产生一个 hooks 对象,hooks 对象中,保存着当前 hooks 的信息,不同 hooks 保存的形式不同。每一个 hooks 通过 next 链表建立起关系。
-
** React Hooks 为什么不能写在条件语句中?**
因为在更新过程中,如果通过 if 条件语句,增加或者删除 hooks,在复用 hooks 过程中,会产生复用 hooks 状态和当前 hooks 不一致的问题。
fiber树结构是双缓存树结构,所以如果workInProgress中的hook因为条件语句的问题没执行,当第二次渲染在current树中可能会发现hook类型不一致的问题,导致报错。
-
useMemo 内部引用 useRef 为什么不需要添加依赖项,而 useState 就要添加依赖项。
-
useEffect 添加依赖项 props.a ,为什么 props.a 改变,useEffect 回调函数 create 重新执行。
如果函数组件需要更新副作用,会标记 UpdateEffect,至于哪个effect 需要更新,那就看 hooks 上有没有 HookHasEffect 标记,所以初始化或者 deps 不想等,就会给当前 hooks 标记上 HookHasEffect ,所以会执行组件的副作用钩子。
-
React 内部如何区别 useEffect 和 useLayoutEffect ,执行时机有什么不同?
React 会用不同的 EffectTag 来标记不同的 effect,对于useEffect 会标记 UpdateEffect | PassiveEffect, UpdateEffect 是证明此次更新需要更新 effect ,HookPassive 是 useEffect 的标识符,对于 useLayoutEffect 第一次更新会打上 HookLayout 的标识符。React 就是在 commit 阶段,通过标识符,证明是 useEffect 还是 useLayoutEffect ,接下来 React 会同步处理 useLayoutEffect ,异步处理 useEffect 。
Hooks 出现本质上原因是:
- 1 让函数组件也能做类组件的事,有自己的状态,可以处理一些副作用,能获取 ref ,也能做数据缓存。
- 2 解决逻辑复用难的问题。
- 3 放弃面向对象编程,拥抱函数式编程。
为什么函数组件 useState 改变相同的值,组件不更新了。
当每一次改变 state ,底层会做这些事。
- 首先用户每一次调用 dispatchAction(比如如上触发 setNumber )都会先创建一个 update ,然后把它放入待更新 pending 队列中。
- 然后判断如果当前的 fiber 正在更新,那么也就不需要再更新了。
- 反之,说明当前 fiber 没有更新任务,那么会拿出上一次 state 和 这一次 state 进行对比,如果相同,那么直接退出更新。如果不相同,那么发起更新调度任务。