Jacky's blog Jacky's blog
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Jacky Wang

行到水穷处,坐看云起时
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 编码专题

  • 深入浅出 Vite

  • 快速上手 API

  • 深入浅出Babel

  • 深入浅出 React

    • React源码-常量定义
    • React源码-WorkInProgress常用操作
    • React源码-目录结构
    • React源码-workInProgress详解
    • React源码-ReactFiberWorkLoop.js
      • 0. 前言
      • 1.completeUnitOfWork 逻辑分析
        • completeWork 主逻辑
        • completeWork 返回值
        • completeUnitOfWork 跳出逻辑
    • React源码-completeWork
    • React源码-commit前后阶段工作
    • React源码-commit核心
  • 百问掘金
  • 深入浅出 React
wangjiasheng
2023-07-01
目录

React源码-ReactFiberWorkLoop.js

# 0. 前言

# 1.completeUnitOfWork 逻辑分析

归阶段的核心函数:complteUnitOfWork 、completeWork

在分析源码之前,需要知道考虑到以下两类 DOM 结构,对于 completeWork 遍历逻辑是不同的。

  • 结构1:

    function App() {
      return (
        <div className="App">
          <header className="App-header">
            <img src={logo} className="App-logo" alt="logo" />
          </header>
        </div>
      );
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  • 结构2:

    function App() {
      return (
        <div className="App">
          <header className="App-header">
            <img src={logo} className="App-logo" alt="logo" />
            <p>
              Edit <code>src/App.js</code> and save to reload.
            </p>
            <a
              className="App-link"
              href="https://reactjs.org"
              target="_blank"
              rel="noopener noreferrer"
            >
              Learn React
            </a>
          </header>
        </div>
      );
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

深度优先遍历的逻辑:

  1. 无论是上述两类结构,第一个进入complteWork 的节点始终都是 <img /> 节点。

  2. 对于第一类结构,就是典型的 do...while 语法,且不会跳出循环。

    <img /> →\rightarrow→ <header /> →\rightarrow→ <div />

    伪代码结构如下:

    var completedWork = unitOfWork; // <img /> 节点
    do{
      var returnFiber = completedWork.return;
      // 生成 DOM 节点并插入 stateNode
      completeWork(current, completedWork, subtreeRenderLanes);
      // 返回 父 Fiber 节点
      completedWork = returnFiber;
    }while(completedWork)
    
    1
    2
    3
    4
    5
    6
    7
    8

    而对于第二类结构,会检查是否存在兄弟节点,如果存在会跳出循环逻辑。

    // 是否存在兄弟节点需要深度优先遍历?
    const siblingFiber = completedWork.sibling;
    // 如果存在,则跳出循环
    if (siblingFiber !== null) {
      workInProgress = siblingFiber;
      return;
    };
    
    1
    2
    3
    4
    5
    6
    7
  3. 截止条件:当父节点仍为 null 时,说明已经回到顶部节点(此处为 App 节点),则跳出循环。

完整源码分析:

function completeUnitOfWork(unitOfWork) {
  // 当前待处理的 workInProgress 节点【注:此时 stateNode = null】
  var completedWork = unitOfWork;
  
  do{
    // 获取 current Fiber
    var current = completedWork.alternate;
    // 提前缓存父节点指针
    var returnFiber = completedWork.return;
    
    // completedWork.flags 记录当前是否完成遍历
    if((completedWork.flags & Incomplete) === NoFlags){
      // @todo
      setCurrentFiber(completedWork);
      var next = void 0; // undefined
      
      // completedWork.mode // 若为同步模式为 `BlockingMode` ,异步为 `ConcurrentyMode`
      if ((completedWork.mode & ProfileMode) === NoMode) {
        // 进入性能模式
        next = completeWork(current, completedWork, subtreeRenderLanes);
      } else {
        // 一般模式
        next = completeWork(current, completedWork, subtreeRenderLanes); // Update render duration assuming we didn't error.
      }
      
      // complteWork 的返回值, 当不为 null 时跳出循环
      if(next !== null){
        workInProgress = next;
        return;
      }
      
      if(returnFiber !== null && (returnFiber.flags & Incomplete) === NoFlags){
       // 此阶段用于生成 EffectList 链表 
      }
    }
    
    // 是否存在兄弟节点需要深度优先遍历?
    const siblingFiber = completedWork.sibling;
    // 如果存在,则跳出循环
    if (siblingFiber !== null) {
      workInProgress = siblingFiber;
      return;
    };
    
    // 返回 父 Fiber 节点
    completedWork = returnFiber;
    // 修改全局 workInProgress 对象
    workInProgress = completedWork;
  }while (completedWork !== null);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

# completeWork 主逻辑

位置:【react-reconciler】【ReactFiberComplteWork.old.js】

complteWork 核心代码逻辑就是 switch...case

function completeWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
): Fiber | null {
    switch (workInProgress.tag){
      /** 对于以下节点是不予以处理的 */ 
      case IndeterminateComponent:
      case LazyComponent:
      case SimpleMemoComponent:
      case FunctionComponent:
      case ForwardRef:
      case Fragment:
      case Mode:
      case Profiler:
      case ContextConsumer:
      case MemoComponent:
        return null;
      }
      case xxx;
    	  return null;
      case HostComponent;
        /** 见后续分析 */
    		return null
      ....
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

complteWork 的核心功能就是往 fiber 上挂载 stateNode,但对于 fiber 结构并非与 DOM 结构一一对应的,如 FunctionComponent 和 Fragment 是典型不产生DOM ,只是作为一种 namespace 存在。

举例:

function A(){
  return <B />
}
  
function B(){
  return <C />
}
  
function C(){
  return <h1>Hello world</h1>
}
1
2
3
4
5
6
7
8
9
10
11

对其中一个 case 进行分析:

case HostComponent: {
  // 很重要:获取 `root` 对应的 DOM 节点, 如 React.render(<App>, rootDOM) 
	const rootContainerInstance = getRootHostContainer();
  // img 对应的 tag 标签为 5
  const type = workInProgress.type;
  // 更新逻辑:current 不为空,且已生成对应的 DOM 节点【对于 HostComponent 是存在 DOM 结构的】
  if(current !== null && workInProgress.stateNode != null){
    // 更新 HostComponent 逻辑
    updateHostComponent(
      current,
      workInProgress,
      type,
      newProps,
      rootContainerInstance,
    );
    
    // @todo: 后续理解
    if (current.ref !== workInProgress.ref) {
      markRef(workInProgress);
    }
  }else{
    // 找到当前的 Context 环境
    const currentHostContext = getHostContext();
    
    if (wasHydrated) {
      /** @todo: SSR 逻辑暂不看 */
    }else{
      // 生成 DOM 结构
      const instance = createInstance(
        type,
        newProps,
        rootContainerInstance,
        currentHostContext,
        workInProgress,
      );
      
      // 插入 DOM 结构中
      appendAllChildren(instance, workInProgress, false, false);
      // 把 DOM 结构缓存在 Fiber 上
      workInProgress.stateNode = instance;
      
      if(
        finalizeInitialChildren(
          instance,
          type,
          newProps,
          rootContainerInstance,
          currentHostContext,
        )
      ){
        // 标记需要更新节点
        markUpdate(workInProgress);
      }
    }
    if (workInProgress.ref !== null) {
      // 标记需要处理 ref
      markRef(workInProgress);
    }
  }
  return null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

上面中用到了两个函数,用于操作 flags ,后续将此部分统一放到 ReactFiberTag 那篇文章中。

function markUpdate(workInProgress) {
  workInProgress.flags |= Update;
}

function markRef(workInProgress) {
  workInProgress.flags |= Ref;
  {
    workInProgress.flags |= RefStatic;
  }
}
1
2
3
4
5
6
7
8
9
10

对于其中的 mount 阶段核心函数:

  • createInstance:构建节点。
  • appendAllChildren:

# completeWork 返回值

对于大部分 complteWork 的返回值均为 null

只有少数几个 case 会返回 Fiber 结构,错误处理或 SuspendCompnent 处理。

case SuspenseComponent:
  ....
  if ((workInProgress.flags & DidCapture) !== NoFlags) {
    // Something suspended. Re-render with the fallback children.
    workInProgress.lanes = renderLanes;
    return workInProgress;
  }

case SuspenseListComponent:
   ....
   return workInProgress.child;
1
2
3
4
5
6
7
8
9
10
11

# completeUnitOfWork 跳出逻辑

  • 同步逻辑代码:

最外层代码逻辑:只要 workInProgress 不为 null 会一直触发 performUnitOfWork

注:这里 workInProgress 一直是一个全局变量,一般在代码中处理 workInprogress 会先拷贝一份再处理。

function workLoopSync() {
  while (workInProgress !== null) {
    // render => render => completeWork => render => completeWork => completeWork
    performUnitOfWork(workInProgress);
  }
}
1
2
3
4
5
6
  • performUnitOfWork 逻辑

此函数是 reconciler 协调器的核心循环逻辑,beiginWork 是除 fiberRoot 外节点的核心代码,只要 workInProgress.child 能被成功创建,beginWork 函数就支持一直递归循环下去。

beginWork 的截止条件,会进入 completeWork 的处理逻辑。

function performUnitOfWork(unitOfWork: Fiber): void {
  // unitOfWork 就是 workInProgress
  const current = unitOfWork.alternate;

  let next;
  // 创建并返回 workInProgress.child
  next = beginWork(current, unitOfWork, subtreeRenderLanes);

  // 将当前的 workInProgress 的 props 缓存在 `memoziedProps` 上
  unitOfWork.memoizedProps = unitOfWork.pendingProps;
  if (next === null) {
    // beginWork => completeWork 
    completeUnitOfWork(unitOfWork);
  } else {
    // beginWork => beiginWork(next节点)
    workInProgress = next;
  };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

completeWork 的退出条件,就是查看是否一直向上遍历,直至到根节点,此时 workInProgress 为空,则跳出 performUnitOfWOrk 的处理逻辑。

编辑 (opens new window)
上次更新: 2023/09/17, 22:09:00
React源码-workInProgress详解
React源码-completeWork

← React源码-workInProgress详解 React源码-completeWork→

最近更新
01
如何理解浏览器的 user agent 用户代理的含义?
11-05
02
浏览器事件循环机制
10-31
03
浏览器页面渲染机制【2023】
10-15
更多文章>
Theme by Vdoing | Copyright © 2020-2023
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式