🔥【图解 React】🚀Vue 转 React 必备🚀(二)React 18 重渲染市面上的某些源码解析,没有一个
前言
- 市面上的某些源码解析,没有一个真实的案例把整个
react
流程串起来。导致看了许多所谓的源码,反而把自己看懵了 - 所以本系列文章全部从实际案例出发,尽力让零基础未接触
React
的人都能上手源码 - 本人在未做过
react
项目的前提下先学的源码,所以各位大神应该可以无痛上手
如果有帮忙到的请帮忙给小弟点赞收藏,非常感谢!
🤖 代码实例
<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>
}
🤖 点击 <div onClick={handleClick}/>
🚀 调用 setState(false)
,触发重渲染
function setState(value) {
1. 创建一个更新
const update = {
action: value
}
2. 触发重渲染,应用更新于当前 fiber 树,产生新的 fiber 树
scheduleUpdateOnFiber()
}
🤖 render
阶段
🚀 创建新 fiber 树的第一个节点
- 诶? 当前页面对应的
fiber
树的alternate
属性刚好是一个 fiber 节点 - 不用白不用,刚好作为新
fiber
树的第一个节点 - 顺便把当前页面的
HostRootFiber
上的属性浅拷贝给我注意:由于浅拷贝了,所以我的 fiber.child 指向了当前的 <App /> Fiber
🚀 由上往下对每个 fiber
执行 beginWork
- 对
HostRootFiber
执行beginWork
function beginWork() {
if (函数组件新旧属性一样 && 函数组件没有执行过 setState) {
1. 复用当前页面的子节点,也就是 <App /> Fiber // const (新 <App /> Fiber) = cloneFiber(fiber.child) // 看第一张图红色的线
// 复用的意思,创建一个新的 <App /> Fiber,但是他的属性是浅拷贝旧 <App /> Fiber 的
2. return <App /> Fiber
}
...
}
- 对
App Fiber
执行beginWork
- 重新执行了
App
组件的函数,所以App
就是重渲染React 浏览器插件可以看到这个 <App /> 组件闪了一下红色
function beginWork() {
// <App /> 执行过 setState,不进入这个逻辑
if (函数组件新旧属性一样 && 函数组件没有执行过 setState) {}
switch (fiber.tag) {
case FunctionComponent: {
1. 重新执行函数组件(fiber.type),重新执行 useState, 拿到最新值 state =
false, 重新执行 __jsx("div",...), 拿到新的 div JSX
// function App() {
// const [state, setState] = useState(true);
// return _jsx("div", {
// onClick: handleClick,
// children: state ? _jsx(Son, {}) : _jsx(Daughter, {})
// });
// }
2. div jsx 跟 当前页面的 Fiber 进行 dom-diff, 都是 div,复用 div Fiber
3. 返回 Div Fiber
}
}
}
- 对
div Fiber
执行beginWork
function beginWork() {
//<App /> 函数重新执行,导致重新执行 _jsx(), 导致 div 的 props 属性变了,不进入这个逻辑
if (函数组件新旧属性一样 && 函数组件没有执行过 setState) {}
switch (fiber.tag) {
case HostComponent: {
1. 拿到 div fiber 的 pendingProps.children,也就是 <Daughter /> JSX
2. <Daughter /> JSX 跟 <Son /> Fiber 比较,type 不同,不能复用
3. <App /> Fiber 标记 ChildDeletion 副作用,并把 <Son /> fiber 放到 <App /> 的 deletions 数组里
4. 创建新的 <Daughter /> Fiber,标记 Placement 副作用
5. return <Daughter /> Fiber
}
}
}
- 对
Daughter Fiber
执行beginWork
function beginWork() {
//<App /> 函数重新执行,导致重新执行 _jsx(Daughter, {}), 导致 div 的 props 属性变了,不进入这个逻辑
if (函数组件新旧属性一样 && 函数组件没有执行过 setState) {}
switch (fiber.tag) {
case FunctionComponent: {
1.拿到函数组件本身(fiber.type)执行,拿到返回的 <div/> jsx
2.创建新的 <div /> Fiber
3.return <div/> Fiber
}
}
}
🚀 走到 fiber 树最深处,又由下往上对每个 fiber
执行 completeWork
- 对
Div fiber
执行completeWork
function completeWork() {
switch (fiber.tag) {
case HostComponent: {
// <div /> Fiber 新创建的,当前页面没有对应的 Fiber,所以直接创建
1.通过 document.createElement("div") 【fiber.type】 创建 div dom 节点
2.fiber 上的 stateNode 属性和 div dom 节点相关联
}
}
}
-
对
<Daughter/> fiber
执行completeWork
,没做啥,省略 -
对
Div fiber
执行completeWork
function completeWork() {
switch (fiber.tag) {
case HostComponent: {
// 当前的 <div /> Fiber 上已经有 stateNode 了
对比 div 的新旧属性差异,如果有,还会给 <div /> Fiber 标记 Update 副作用
这里新旧属性props里面的值没有变化,所以没有标记 Update 副作用
}
}
}
- 对
App fiber
执行completeWork
,没做啥,省略 - 对
HostRootFiber fiber
执行completeWork
,没做啥,省略
🤖 commit
阶段
- 对
HostRootFiber Fiber
执行commitMutationEffectsOnFiber
function commitMutationEffectsOnFiber() {
switch (fiber.tag) {
case HostRoot: {
// 递归函数: 里面先对子 <App /> Fiber 执行 commitMutationEffectsOnFiber
recursivelyTraverseMutationEffects()
// 递归完,才处理 HostRootFiber Fiber 自己,这里暂时没做什么
commitReconciliationEffects()
}
}
}
- 对
App Fiber
执行commitMutationEffectsOnFiber
function commitMutationEffectsOnFiber() {
switch (fiber.tag) {
case FunctionComponent: {
// 递归函数: 里面先对子 <div /> Fiber 执行 commitMutationEffectsOnFiber
recursivelyTraverseMutationEffects()
// 递归完,才处理 <App /> Fiber 自己,这里暂时没做什么
commitReconciliationEffects()
}
}
}
- 对
<div /> Fiber
执行commitMutationEffectsOnFiber
- 应用 ChildDeletion 副作用,卸载真实的旧的
dom
节点
function commitMutationEffectsOnFiber() {
switch (fiber.tag) {
case FunctionComponent: {
1. 发现 (<div/> fiber).deletions 数组不为空,遍历 deletions,
把对应的 dom 节点卸载掉(往上找到最近的dom,调用
dom.removeChild()卸载)
2.先对子 <Daughter /> Fiber 执行 commitMutationEffectsOnFiber,
// recursivelyTraverseMutationEffects()
3.递归完,才处理 <div /> Fiber 自己,这里暂时没做什么
// commitReconciliationEffects()
}
}
}
- 对
<Daughter /> Fiber
执行commitMutationEffectsOnFiber
- 引用
Placement
副作用,连接父子dom
节点
function commitMutationEffectsOnFiber() {
switch (fiber.tag) {
case FunctionComponent: {
1.递归函数: 里面先对子 <div /> Fiber 执行 commitMutationEffectsOnFiber
// recursivelyTraverseMutationEffects()
2.递归完,才处理 <Daughter /> Fiber 自己
2.1 发现 <Daughter /> Fiber 有一个 Placement 的副作用
2.2 应用副作用,fiber 树往上遍历找到最近的父 dom 节点,fiber 树往下遍历找到最近的
子 dom 节点,插入进去
// commitReconciliationEffects()
}
}
}
- 对
<div /> Fiber
执行commitMutationEffectsOnFiber
,没做啥省略
版权归许泽川所有
如需转载,请提前询问本人的许可
转载自:https://juejin.cn/post/7412504006340853800