1. 回顾
前几篇文章中,我们采用了 VSCode 插件 CodeTour 来记录代码的执行过程,
 并把相关的数据 .tour/ 放到了 github: thzt/react-tour 中。
截止到本文为之,我们总共记录了这些 code-tour,
.tour/
├── 2. 构建过程.tour
├── 3.1 react 的加载过程.tour
├── 3.2 react-dom 的加载过程.tour
├── 4.1 组件加载过程:函数组件(call stack).tour
├── 4.1.1 组件加载过程:函数组件(全流程).tour
└── 4.2 组件加载过程:类组件(call stack).tour
 
  本文重点介绍 4.1.1 组件加载过程:函数组件(全流程) 相关的内容。
2. 极简的示例项目
现在我们开始介绍 React 函数组件的加载全流程,我们的示例项目如下,
 github: thzt/react-tour/example-project
example-project/
├── README.md
├── package.json
├── public
|  └── index.html
├── src
|  ├── App.js
|  └── index.js
└── yarn.lock
 
  其中,index.js 的内容如下,
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
  <App />,
  document.getElementById('root')
);
 
  App.js 的内容如下,
const App = () => {
  debugger;
  return 'hello world'
};
export default App;
 
  当前 React 项目只加载了一个 <App /> 组件,这个组件只返回了一段纯文本 'hello world'。
与 React 初窥门径(一):环境准备 介绍的一致,我们可以启动项目,
$ yarn
$ yarn start
# http://127.0.0.1:3000
 
  3. 调试 Web 项目
参考 React 初窥门径(三):用 VSCode 调试,
 我们将 github: thzt/react-tour 中的文件,拷贝到 React 源码根目录,
-  package.json:直接覆盖,其中新增了 
debug-build这个 npm scripts -  .vscode/:拷贝到 React 源码目录,其中包含了两个 debug 配置,我们要用 
Debug React这个配置 - .tour/:VSCode CodeTour 插件的数据
 
整体操作流程如下:
 (1)示例项目操作过程
- 下载 github: thzt/react-tour/example-project 示例项目
 - 给示例项目安装依赖:
yarn - 替换 
example-project中node_modules中的依赖react和react-dom
参考 React 初窥门径(一):环境准备
可使用工具 github: thzt/react-tour/tool/link.bash - 启动示例项目:
yarn start,http://127.0.0.1:3000 
(2)React 源码操作过程
- 下载(克隆) React 源码,并切换到 v17.0.2
 - 拷贝 github: thzt/react-tour 中的 
package.json.vscode/.tour/到 React 源码目录 - 选择 
Debug React选项进行调试 
 
    我们发现 VSCode 的断点停在了 example-project/src/App.js 文件中。
const App = () => {
  debugger;              // <- 断点到了这里
  return 'hello world'
};
export default App;
 
  4. 调用栈
我们先来跟踪一下,从 ReactDOM.render 到 App 组件 debugger 位置的调用栈,
 
 
    在 CodeTour(.tour/)中,也记录了这个过程,
 
 
    4.1 组件加载过程:函数组件(call stack)
render
legacyRenderSubtreeIntoContainer
unbatchedUpdates
fn
updateContainer
scheduleUpdateOnFiber
performSyncWorkOnRoot
renderRootSync
workLoopSync
performUnitOfWork
beginWork$1
beginWork
mountIndeterminateComponent
renderWithHooks
 
  以上调用栈只展示了函数的链式调用关系,如果用缩进表示调用链路的话,它应该是这样的,
render
  legacyRenderSubtreeIntoContainer
    unbatchedUpdates
      fn
        updateContainer
          scheduleUpdateOnFiber
            performSyncWorkOnRoot
              renderRootSync
                workLoopSync
                  performUnitOfWork
                    beginWork$1
                      beginWork
                        mountIndeterminateComponent
                          renderWithHooks
 
  它表示 render 调用了 legacyRenderSubtreeIntoContainer,
 legacyRenderSubtreeIntoContainer 又调用了 unbatchedUpdates,
 unbatchedUpdates 又调用了 fn 等等,直到最后调用了 renderWithHooks。
最后 renderWithHooks 调用了函数组件 App,来到断点那里。
5. 全流程
只看调用栈的话,React 组件的加载过程还不完整,我们知道某个函数之前别调用之前,是否还调用了其他函数,
 以下我们整理了从 render 到 App 调用的全流程(函数前面的数字,表示缩进层次)。
4.1.1 组件加载过程:函数组件(全流程)
 (下图包含代码折叠,而且只截了一部分,完整版请查看上面的链接)
 
    6. render 和 commit 阶段
全流程包含了特别多的细碎逻辑,我们首先想弄明白的是,
- 组件是何时被调用的,组件返回之后发生了什么(render 阶段)
 - 组件是如何展示在页面上的(commit 阶段)
 
这两个阶段,就是 performSyncWorkOnRoot 做的事情了,在大图中它处于这个位置,
 
 
    可以看到:
-  render 阶段(
renderRootSync):根据用户创建的 React 组件,创建 Fiber Tree(先从上到下performUnitOfWork,再从下到上completeWork) -  commit 阶段(
commitRoot):把 Fiber Tree 实际写入到 DOM 中 
一图胜千言,(函数前面的数字,表示缩进层次)
[6] performSyncWorkOnRoot
  [7] renderRootSync
    [8] markRenderStarted                                         <- render 阶段开始
    [8] workLoopSync
      [9] performUnitOfWork ---- [HostRoot {tag: 3}]              <- 从 根元素 开始向下构建 Fiber Tree
        [10] beginWork$1
          [11] beginWork
            [12] updateHostRoot                                   <- 加载 根元素 HostRoot
              [13] reconcileChildren
                [14] reconcileChildFibers                         <- 创建 child 子元素
                  [15] reconcileSingleElement
                    [16] createFiberFromElement
                      [17] createFiberFromTypeAndProps
                        [18] createFiber
      [9] performUnitOfWork ---- [IndeterminateComponent {tag: 2}] (<App />)
        [10] beginWork$1
          [11] beginWork
            [12] mountIndeterminateComponent                      <- 加载 函数组件 App
              [13] renderWithHooks
                [14] Component
              [13] reconcileChildren
                [14] mountChildFibers=reconcileChildFibers        <- 创建 child 子元素
                  [15] reconcileSingleTextNode
                    [16] deleteRemainingChildren
                    [16] createFiberFromText
                      [17] createFiber
      [9] performUnitOfWork ---- [HostText {tag: 6}] ('hello world')
        [10] beginWork$1
          [11] beginWork
            [12] updateHostText                                   <- 加载 纯文本 'hello world'
        [10] completeUnitOfWork                                   <- 开始倒着从 子节点 向上到 根节点 进行梳理
          [11] completeWork ---- [HostText {tag: 6}] ('hello world')
            [12] createTextInstance
              [13] createTextNode
                [14] createTextNode [HTMLElement] ('hello world') <- 实际创建 HTML
          [11] completeWork ---- [IndeterminateComponent {tag: 2}] (<App />)
          [11] completeWork ---- [HostRoot {tag: 3}]
    [8] markRenderStopped                                         <- render 阶段结束
  [7] commitRoot                                                  <- commit 阶段开始
 
  下文我们再仔细介绍 commit 阶段。
参考
github: facebook/react v17.0.2
 VSCode: CodeTour
 github: thzt/react-tour
 github: thzt/react-tour/example-project
 React 初窥门径(一):环境准备
 React 初窥门径(三):用 VSCode 调试
 4.1 组件加载过程:函数组件(call stack)
 4.1.1 组件加载过程:函数组件(全流程)
 
喜欢的朋友记得点赞、收藏、关注哦!!!

















