🔥【图解 React】🚀Vue 转 React 必备🚀(一)React 18 初次渲染市面上的某些源码解析,没有一
前言
市面上的某些源码解析,没有一个真实的案例把整个 react
流程串起来。导致看了许多所谓的源码,反而把自己看懵了
所以本系列文章全部从实际案例出发,尽力让零基础未接触 React
的人都能上手源码
本人在未做过 react
项目的前提下先学的源码,所以各位大神应该可以无痛上手
文章中多次提及的
react
源码里面的几个关键函数名,和fiber
上的一些关键属性名,还有之后可能提及的一些优先级车道相关的东西。只要了解了,我觉得应该是能够初步具备调试
React
源码的能力的。比如之后可能要写的React.lazy
和<Suspense />
和<OffScreen />
组件关系的文章,useContext
性能问题文章等,就是我自己调试源码总结出来的。
如果有帮忙到的请帮忙给小弟点赞收藏,非常感谢!
🤖 代码实例
<html lang="en">
<body>
<div id="root"></div>
</body>
</html>
function App() {
const [state, setState] = useState(true);
function handleClick() {
setState(pre => !pre)
}
return <div onClick={handleClick}>
{
state ? <Son/> : <Daughter />
}
</div>
}
function Son() {
return <div id={'son'}></div>
}
function Daughter() {
return <div id={"Daughter"}></div>
}
🤖 初始化阶段(从这里开始看)
const root = createRoot(document.getElementById('root'))
root.render(<App />)
🚀 createRoot(document.getElementById('root'))
做了什么?
- 创建
HostRootFiber
这个fiber
节点
当前
html
文件只有一个<div id="root" />
,所以这个fiber
节点其实就是第一颗fiber
树
- 将
HostRootFiber fiber
和DOM(#root)
相关联
🚀 root.render(<App>)
做了什么?
- 安排一个更新
{element: __jsx(App, {})}
更新内容:根据
App jsx
创建出App fiber
,挂载在HostRootFiber
下
- 触发重渲染,来处理这个更新,生成新的
fiber
树,来替换当前的fiber
树
howdy ho 🙋 im the first fiber tree
🤖 render 阶段
🚀 创建新 fiber
树的第一个节点
创建一个全新的 HostRootFiber
,但是复用了旧 HostRootFiber
上的所有属性。双方通过 alternate
互相关联
最后所有工作完成,才去修改current指针到新的
fiber
树完成fiber
树的切换
🚀 由上往下对每个 fiber
执行 beginWork
- 对
HostRootFiber
执行beginWork
,创建出子节点App Fiber
- 标记
App Fiber
的flags
为Placement
function BeginWork() {
switch (fiber.tag) {
case HostRoot:
1.上文说到的创建更新,应用更新,将 HostRootFiber.memoizedState = {element:
__jsx(App, {})}
2.拿到 <App/>的jsx (也就是 HostRootFiber.memoizedState)
3.当前 fiber 树没有 <App /> Fiber,根据 <App /> jsx 创建新的 <App /> fiber
4.正在构建(workInProgress)的这个 HostRootFiber 的 alternate 不为空(看图:
当前的页面(current)对应的 fiber 树本来就有一个 HostRootFiber)
4.所以给 <App /> fiber 标记一个插入的副作用,表示等一下要把 <App/> 插入到
HostRootFiber 下 fiber.flags |= Placement
5.返回 <App /> fiber
}
}
对 App fiber
执行 beginWork
,创建出子节点 div Fiber
function BeginWork() {
switch (fiber.tag) {
case FunctionComponent: {
1.拿到函数组件本身(fiber.type)执行,拿到返回的 <div/> jsx
2.jsx 跟 当前页面的 Fiber 进行 dom-diff,决定复用或者创建对应的fiber
3.返回复用或者新创建的 <div/> Fiber
}
}
}
对 div Fiber
执行 beginWork
,创建出子节点 Son Fiber
function BeginWork() {
switch (fiber.tag) {
case HostComponent: {
1.拿到 props.children, 也就是 <Son /> jsx 对象
2.当前 fiber 没有 <Son /> Fiber,根据 <App /> jsx 创建新的 <App /> fiber
3.返回 <Son /> Fiber
}
}
}
同上
🚀 走到 fiber 树最深处,又由下往上对每个 fiber
执行 completeWork
对 Div fiber
执行 completeWork
function completeWork() {
switch (fiber.tag) {
case HostComponent: {
1.通过 document.createElement("div") 【fiber.type】 创建 div dom 节点
2.fiber 上的 stateNode 属性和 div dom 节点相关联
}
}
}
对 Son fiber
执行 completeWork
,没做啥,省略
对 Div fiber
执行 completeWork
3. 执行 completeWork
function completeWork() {
switch (fiber.tag) {
case HostComponent: {
1.判断 fiber.stateNode 为空
2.通过 document.createElement("div") 创建 div dom 节点
3.顺便把所有子 dom 节点挂到自己身上 (dom.appendChild)
// 当页面没有对应dom的时候有一个小优化:这个递归流程,刚好能把子dom都插入到自
身,之后能省掉这一步操作
// 当前fiber是新创建的,也就是没有旧fiber,不用担心appendChild到当前页面上真
实的dom造成页面闪烁,所以能优化
4.fiber 上的 stateNode 属性和 div dom 节点相关联
}
}
}
对 App fiber
执行 completeWork
,没做啥,省略 对 HostRootFiber fiber
执行 completeWork
,没做啥,省略
🤖 commit
阶段
🚀 递归执行 commitMutationEffectsOnFiber
🚀 应用 fiber
上的副作用 flags
,创建真实 dom
,或者对 dom
增删查改
function schedulerUpdateOnFiber() {
}
版权归许泽川所有
如需转载,请提前询问本人的许可
TODO 后续文章 1篇/1周
react 重渲染
加餐:重渲染的优先级
加餐:React 重渲染优化,批量更新
加餐:useContext vs 外部数据源
加餐:React 闭包陷阱
加餐:新手误区,何时使用 useState 何时使用 useRef
dom-diff
React.lazy 和 Suspense 原理
加餐:原生 keep-alive 组件 OFFSCREEN
useEffect/useLayoutEffect
REACT 常用工具
转载自:https://juejin.cn/post/7411168518751862825