)
教程前端文档【免费下载链接】Under-the-hood-ReactJSEntire React code base explanation by visual block schemes (Stack version)项目地址https://gitcode.com/gh_mirrors/un/Under-the-hood-ReactJS点击查看免费下载导读本文基于 Under-the-hood-ReactJS 仓库《Stack Reconciler》系列的第 3 部分深入剖析 React 15.4.2Stack 版本在浏览器中执行组件挂载Mount的核心路径——ReactCompositeComponent.mountComponent。文章以可视化流程图stack/images/3/part-3.svg为线索逐一拆解组件树如何从上到下递归挂载、updater为何在挂载时才动态赋值给实例、自定义组件实例何时被new出来、componentWillMount中的setState为何不会触发重渲染以及首个子 VDOM 实例如何被实例化五个关键问题。读完本文你将掌握从ReactDOM.render到自定义组件首次被触碰再到子元素ReactDOMComponent挂载的完整调用链能够独立读懂 ReactCompositeComponent.js 中对应的源码片段并为后续理解setState更新机制Part 9打下基础。回顾上下文我们正处在挂载链路的哪一环在进入本文之前需要明确当前的代码坐标。整个 ReactDOM 挂载流程被拆解为 15 个部分见 README.md 与 Intro.mdPart 3 之前我们已经完成了事务Transaction机制Part 1ReactDOM.render被包裹在ReactDefaultBatchingStrategyTransaction中其close阶段会调用ReactUpdates.flushBatchedUpdates保证挂载完成后统一校验 dirty 组件。ReactReconcileTransactionPart 2通过SELECTION_RESTORATION、EVENT_SUPPRESSION、ON_DOM_READY_QUEUEING三个 wrapper 保持 DOM 状态如文本选区、事件抑制随后ReactReconciler.mountComponent作为平台相关逻辑的中介把挂载任务委托给真正的组件模块。而 Part 3 的主角正是被ReactReconciler委托到的那位ReactCompositeComponent.mountComponent——文档原话称它是我们整个旅程中最大的部分之一。递归挂载从 TopLevelWrapper 到第一个真实组件组件树的入口TopLevelWrapper需要先纠正一个常见直觉组件树中被第一个 push 进树的并不是你的应用组件而是一个 React 内部类TopLevelWrapper。它是空的包装器不对流程产生任何影响因此文档建议直接跳过它从其子组件开始观察。这也揭示了挂载的本质规律挂载是一棵树的深度优先递归过程——先挂载父组件再挂载它的孩子再挂载孩子的孩子依次类推。文档明确断言TopLevelWrapper挂载完成后它的子组件即负责管理ExampleApplication的那个ReactCompositeComponent实例会被立即推入同一阶段继续处理。所以第 (1) 步ReactCompositeComponent.mountComponent的执行对象就是管理ExampleApplication的内部实例。ReactCompositeComponent.mountComponent(1) │ ├─ (2) updater transaction.getUpdateQueue() → 赋给 inst.updater ├─ (3) this._constructComponent() → new ExampleApplication() ├─ (4) 初始挂载componentWillMount → render └─ (5) this._instantiateReactComponent() → 为 render 返回的元素创建 VDOM 实例为实例动态注入 updater跨平台的关键设计updater 是什么ReactCompositeComponent中第一件关键动作是把transaction.getUpdateQueue()的返回值即ReactUpdateQueue模块赋给组件实例的updater字段图中步骤 (2)。为什么要在挂载时动态赋值而不是在类定义中写死文档给出了清晰解释ReactCompositeComponent是所有平台共用的类而 updater 因平台而异因此我们根据平台在挂载期间动态地将其赋给实例。React 需要同时支撑浏览器ReactDOM、移动端ReactNative、服务端渲染、ReactART 等环境见 Intro.md。同一份ReactCompositeComponent无法内置某个平台的更新策略于是谁来负责把setState排队、合并、调度这件事被推迟到挂载阶段由当前平台的 transaction 提供。这是典型的依赖注入式设计公共类不关心平台差异平台差异在运行时被塞进来。不只是 updaterprops / context / refs 一同就位文档特别强调这一阶段为实例赋值的并不仅有updater。原文档给出了ReactCompositeComponent.js中对应的源码片段原文档标注为#255附近// src/renderers/shared/stack/reconciler/ReactCompositeComponent.js#255 // 这些应该在构造方法里赋值但是为了 // 使类的抽象更简单我们在它之后赋值。 inst.props publicProps; inst.context publicContext; inst.refs emptyObject; inst.updater updateQueue;也就是说你的组件实例在挂载阶段被一次性补齐了四个核心成员字段来源说明inst.propspublicProps父级传入的属性之后可通过this.props读取inst.contextpublicContextReact context 上下文inst.refsemptyObject初始为空对象的 refs 集合待子组件挂载后填充inst.updaterupdateQueue平台相关的更新队列setState的底层执行者正因如此你才可以在自己的代码里直接写this.props——这正是上述赋值语句生效的直观结果。值得牢记updater现在虽用不上但它即将在setState中被使用。Stack 版本中setState的本质是把状态变更enqueue进updaterReactUpdateQueue.enqueueSetState再由批处理事务统一 flush。理解这一点就为阅读 Part 9setState 更新起点 埋下了伏笔。实例化你的组件new ExampleApplication() 第一次被调用_constructComponent 的调用链图中步骤 (3)this._constructComponent是我们的代码第一次被 React 生态触碰的里程碑。经过若干构造辅助方法文档描述为several construction methods后最终执行new ExampleApplication()——此时你写在构造函数里的逻辑才第一次真正运行。这一点对调试者意义重大从ReactDOM.render走到这里此前所有步骤都是 React 内部机制在运转事务、包装、实例化内部类直到_constructComponent才首次进入用户代码。文档对此的评价是Nice.——这是用户代码与 React 生态的第一次握手。执行首次挂载componentWillMount 与 state 的特殊处理生命周期钩子的出场顺序步骤 (4) 进入初始挂载initial mount第一个发生的行为是调用componentWillMount仅当组件定义了该方法时。这是全书遇到的第一个生命周期钩子。文档同时指出componentDidMount此刻并没有被直接调用而是被 push 进事务队列ON_DOM_READY_QUEUEINGwrapper 的职责之一要等所有挂载操作全部完成、事务close时才真正执行。这也解释了为什么componentDidMount总是晚于所有子组件挂载完毕——它被推迟到了挂载事务收尾阶段。componentWillMount 里调用 setState只算 state不触发 render原文档引用了官方文档的原话来佐证行为componentWillMount()is invoked immediately before mounting occurs. It is called beforerender(), therefore setting state in this method will not trigger a re-rendering.随后给出了源码级验证ReactCompositeComponent.js#476附近// src/renderers/shared/stack/reconciler/ReactCompositeComponent.js#476 if (inst.componentWillMount) { //.. inst.componentWillMount(); // 当挂载时在 componentWillMount 中调用的 setState // 会设置 this._pendingStateQueue 而不触发重渲染 if (this._pendingStateQueue) { inst.state this._processPendingState(inst.props, inst.context); } }这里有两个值得展开的机制细节_pendingStateQueuecomponentWillMount内调用setState并不会直接改写inst.state而是把待更新的 state 对象排入this._pendingStateQueue。_processPendingState挂载分支随后检测到_pendingStateQueue存在立即调用_processPendingState(inst.props, inst.context)把队列中的多个 partial state 按序合并一次性重新计算inst.state。为什么不调用render也合理因为组件此时尚未挂载完成立刻重渲染既无意义也无处安放结果React 用合并 pending state 延后渲染的方式既让开发者能在componentWillMount里安全地预置初始状态又避免了无谓的重复渲染开销。state 重算之后调用你的 renderstate重算完成后流程继续调用组件中开发者书写的render方法——这是我们的代码第二次被触碰。render返回的 JSX 描述在我们的示例中是一个div将成为下一步创建 VDOM 实例的输入。创建子元素 VDOM 实例从元素到 ReactDOMComponent_instantiateReactComponent 的第二次登场图中步骤 (5)this._instantiateReactComponent此前已经出现过一次在 Part 3 之前它曾为ExampleApplication实例化出ReactCompositeComponent。这一次它被再次调用目的不同上一次为ExampleApplication组件元素创建ReactCompositeComponent内部实例这一次基于render返回的元素为它的孩子创建 VDOM 实例。在我们的具体场景中render返回的是div因此其 VDOM 表示是ReactDOMComponent注意中文翻译版文档此处写作ReactDOMElement英文原版为ReactDOMComponent应以 Part-3.md 原文为准。再次进入 ReactReconciler.mountComponent实例创建完成后代码再次调用ReactReconciler.mountComponent但这次传入的internalInstance是新创建的ReactDOMComponent实例随后继续调用它的mountComponent……如此递归向下挂载将持续深入直到叶子节点真实 DOM 元素的创建在 Part 4 中展开。这就是整棵组件树被从上到下、递归深入地挂载起来的机制每一层的mountComponent都负责初始化自身 实例化自己的孩子 触发下一层挂载。Part 3 的收尾阶段控制权由此从复合组件管理自定义组件的层移交到宿主组件管理真实 DOM 的层。流程图回顾Part 3 的三种抽象层次原文档在本部分结尾提供了三张递进的示意图均在 stack/images/3/ 目录下分别对应三种抽象层次完整版part-3.svg图 3.0Part 3 全部调用关系的原始流程图包含上述 (1)(5) 全部节点简化版part-3-A.svg图 3.1与整理版part-3-B.svg图 3.2剔除冗余连线、规整排版后的核心流程精华版part-3-C.svg图 3.3提炼出的essential value将直接并入全书最终的mounting总图stack/images/6/overall-mounting-scheme.svg 所在的分支。图 3.0 Part 3 完整流程图SVG 可点击放大小结Part 3 的四个核心结论递归挂载模型挂载 父组件挂载 → 子组件挂载 → 孙组件挂载……TopLevelWrapper仅作入口不产生实际影响。实例注入时机props、context、refs、updater四者均在挂载阶段由ReactCompositeComponent动态赋给自定义实例其中updaterReactUpdateQueue是平台差异的注入点也是未来setState的执行通道。生命周期首秀componentWillMount是第一个被调用的钩子其内部setState只写入_pendingStateQueue并通过_processPendingState合并重算state不触发rendercomponentDidMount被延迟到挂载事务的收尾阶段。实例化与递归交接_constructComponent首次执行new ExampleApplication()用户代码首触_instantiateReactComponent第二次被调用时为render返回的div创建ReactDOMComponent再次经ReactReconciler.mountComponent向下递归——真正的 DOM 元素创建留待 Part 4 讲解。至此Part 3 的挂载核心链路已经清晰。下一步可继续阅读 Part 4子元素挂载props 校验与document.createElement或返回 栈式协调器章节总览 定位自己的阅读进度。赞分享教程前端文档【免费下载链接】Under-the-hood-ReactJSEntire React code base explanation by visual block schemes (Stack version)项目地址https://gitcode.com/gh_mirrors/un/Under-the-hood-ReactJS点击查看免费下载相关推荐10个关键点理解React组件挂载过程Under-the-hood-ReactJS实践指南10个关键点理解React组件挂载过程Under the hood ReactJS实践指南 Under the hood ReactJS是一个通过可视化流程图教程前端文档React组件树遍历终极指南深入Under-the-hood-ReactJS架构解析React组件树遍历终极指南深入Under the hood ReactJS架构解析 Under the hood ReactJS是一个通过可视化流程图详细解教程前端文档Under the hood ReactJS用可视化流程图剖析 React Stack reconciler 的挂载与更新机制Under the hood ReactJS用可视化流程图剖析 React Stack reconciler 的挂载与更新机制 本文基于开源仓库 Under教程前端文档上一篇CANN/asc-devkit设置搬运填充值函数下一篇超强Yi模型并行推理多GPU分布式部署全攻略创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考