ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ReactJS 底层揭秘(第 4 部分):ReactDOMComponent 挂载——复杂标签包装、Props 校验与真实 DOM 元素创建

ReactJS 底层揭秘(第 4 部分):ReactDOMComponent 挂载——复杂标签包装、Props 校验与真实 DOM 元素创建 教程前端文档【免费下载链接】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」系列中文版第 4 部分的技术解析。在第 3 部分中我们走完了自定义组件的挂载ReactCompositeComponent.mountComponent本篇将镜头转向其子节点——ReactDOMComponent.mountComponent沿着流程图拆解「复杂标签包装 → Props 校验 → 创建真实 HTML 元素」这三个关键环节回答一个核心问题React 的虚拟 DOM 到底在哪一刻变成浏览器里那个可被看见的真实div读完后你将掌握 Stack 版协调器中 DOM 组件首次挂载的完整调用路径、内部校验规则以及这些设计在源码中的落点。本篇在整体挂载流程中的位置回顾前文在 第 3 部分 中ExampleApplication组件通过_instantiateReactComponent被实例化为ReactCompositeComponent其render()方法返回了div于是基于该元素创建了ReactDOMComponent实例并再次通过ReactReconciler.mountComponent进入挂载阶段。第 4 部分正是从ReactDOMComponent.mountComponent(transaction, ...)这个入口开始的它负责把一个代表 HTML 标签的虚拟 DOM 实例真正挂载到浏览器中。下面这张就是第 4 部分的完整流程图原书图 4.0建议另开标签页放大阅读与正文对照从流程图可以清晰地读出整个挂载路径我们可以先做一个总览后续小节逐一展开流程步骤关键动作作用01this._tag是否命中复杂标签集合如source、video、select等决定是否需要额外的包装器处理—ReactDOMSelect.mountWrapper(...)trapBubbledEventsLocal入队为复杂标签补充原生行为与事件监听02assertValidProps(this, props)校验内部props的合法性非法即抛异常—validateDOMNesting(this._tag, ...)校验子标签 → 父标签的 DOM 嵌套合法性—transaction.useCreateElement分支判断选择以createElement方式创建元素03ownerDocument.createElement(this._currentElement.type)创建真实 HTML 元素虚拟 DOM 第一次落地为真实 DOM—ReactDOMComponentTree.precacheNode(this, el)将内部实例与真实节点建立关联缓存—this._updateDOMProperties(null, props, transaction)初始化 DOM 属性首次挂载时lastProps为null细节见第 5 部分—lazyTree DOMLazyTree(el)包装元素为「延迟构建的 DOM 树」—this._createInitialChildren(transaction, props, context, lazyTree)创建初始子内容—mountImage lazyTree 将挂载产物mount image返回给上层子元素挂载复杂标签的包装器流程图的第一个判断步骤 01检查this._tag是否属于一个「复杂标签」集合包括source、video、select等原文档中还列举了form、textarea。为什么要单独区分它们因为这类标签无法通过普通的属性/子节点处理一步到位媒体类标签如audio、video需要对每个媒体事件补充更多事件监听器例如给audio标签增加volumechange事件监听表单类标签如select、textarea需要封装一些浏览器原生行为例如select的选项与选中状态、textarea的默认值等。React 为这类元素准备了一批专用的包装器wrappers原书引用的源码位置是 React v15.4.2 的src\renderers\dom\client\wrappers\目录典型代表就是ReactDOMSelect与ReactDOMTextarea此路径是原书对 React 源码的引用并非本仓库文件。从流程图可以看到命中复杂标签后会有两个动作调用包装器的mountWrapper(this, props, ...)把标签的原生行为与 React 的 props 对齐通过transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, ...)把冒泡事件捕获trap推迟入队——事件监听要等 DOM 真正就绪后才绑定这正是事务transaction机制在这里的价值事务的包装器机制可回顾 第 2 部分 中的ReactReconcileTransaction。而在我们的示例场景中render()返回的只是一个简单的div——既不涉及媒体事件也不需要表单原生行为因此直接跳过包装器进入下一步的 Props 校验不需要任何额外处理。Props 验证assertValidProps第二个关键动作步骤 02是调用assertValidProps(this, props)。它的职责很明确确保内部props被设置正确否则就抛出异常把开发者写错的用法在挂载早期就拦截下来而不是等到浏览器报出晦涩的 DOM 错误。原文档给出了一个最典型的例子——dangerouslySetInnerHTML。它通常在我们需要基于一个字符串直接插入 HTML时使用其正确形态必须是一个包含__html键的对象// 正确的用法 div dangerouslySetInnerHTML{{ __html: spanhello/span }} / // 错误对象中缺少 __html 键 div dangerouslySetInnerHTML{{ html: spanhello/span }} /一旦你设置了props.dangerouslySetInnerHTML却忘记提供__html键React 会抛出如下异常原文档原文引用props.dangerouslySetInnerHTMLmust be in the form{__html: ...}. Please visit https://fb.me/react-invariant-dangerously-set-inner-html for more information.props.dangerouslySetInnerHTML必须符合{__html: ...}的形式这正是assertValidProps校验逻辑的一部分——「例如」二字也暗示该校验不止覆盖这一项其整体目标是在进入真正的 DOM 操作之前保证所有内部 props 的形态都是合法、可安全落地的。紧接着流程图中还有一步validateDOMNesting(this._tag, ...)的嵌套校验它验证子标签 → 父标签的层级是否合法例如select下只允许option、optgroup或文本节点规则源自 HTML 解析规范。如果你见过div cannot appear as a descendant of p这类报错就是它在工作。这一机制在 第 0 部分 中作为「有趣的事实」被专门介绍过第 4 部分把它正式纳入了 DOM 组件的挂载路径。创建 HTML 元素虚拟 DOM 第一次变成真实 DOM通过上述校验后流程图进入步骤 03——创建 HTML 元素。这里有一个前置分支判断transaction.useCreateElement当事务配置允许使用createElement方式现代浏览器路径时执行el ownerDocument.createElement(this._currentElement.type);ownerDocument.createElement会实例化出真实的 HTMLdiv。这是整个系列到目前为止的「第一次见面」此前的所有工作——JSX 转元素、实例化ReactCompositeComponent、调用render()、实例化ReactDOMComponent——处理的都只是虚拟的表现形式虚拟 DOM而现在一个真正可以放进页面、可以被用户看见的 DOM 节点诞生了。元素创建之后还有一串收尾动作在流程图中清晰可见ReactDOMComponentTree.precacheNode(this, el)把内部实例this与真实节点el建立关联并缓存。从源码结构看这个关联是后续事件委托、更新时快速定位节点的基础设施this._updateDOMProperties(null, props, transaction)初始化 DOM 属性。注意第一个参数是null——因为首次挂载时「上一个 props」还不存在第 5 部分正是专门讲lastProps/nextProps两轮循环的 diff 更新这里只是伏笔lazyTree DOMLazyTree(el)把新建元素包装成「延迟构建」的 DOM 树结构从命名和它在流程图中的位置可以推断这是为了把子内容的创建与实际插入解耦避免过早触发浏览器布局this._createInitialChildren(transaction, props, context, lazyTree)为元素创建初始子内容示例div里的按钮、文本、子组件等会在这一环节继续递归挂载这也是后续部分的内容mountImage lazyTree 挂载产物mount image作为返回值逐层向上传递最终由整个挂载流程的收尾阶段将其插入到容器中这一「最终插入」发生在整体挂载事务的尾部。回顾与提炼第 4 部分的本质现在让我们像原书一样把流程图「瘦身」。先去掉冗余的次要分支得到简化版再调整间距与对齐最终第 4 部分的本质可以浓缩为下面这张图——判断标签复杂度 → 校验 props 与嵌套合法性 → 创建真实 DOM 元素 → 建立实例关联并初始化至此我们完成了对ReactDOMComponent.mountComponent的完整拆解。下一部分第 5 部分将紧接着展开_updateDOMProperties里两轮 props 循环的 diff 更新细节——那里是 React 挂载性能优化的关键一环。对照阅读与延伸如果你想继续深入本仓库中与本篇强相关的资源如下本篇中文原文档stack/languages/chinese/book/Part-4.md英文原版stack/book/Part-4.md上文衔接第 3 部分 · 挂载下文第 5 部分 · 更新 DOM 属性事务与包装器机制背景第 2 部分validateDOMNesting首次登场第 0 部分全套流程图stack/images/4/part-4.svg为完整图part-4-A/B/C.svg为逐级精简图系列导读与示例代码ExampleApplication组件stack/languages/chinese/book/Intro.md上一节第 3 部分 | 下一节第 5 部分 | 返回中文版主页赞分享教程前端文档【免费下载链接】Under-the-hood-ReactJSEntire React code base explanation by visual block schemes (Stack version)项目地址https://gitcode.com/gh_mirrors/un/Under-the-hood-ReactJS点击查看免费下载相关推荐Agentic Awesome Skills 质量标准指南从技能提交到 Validated 徽章的完整验证流程Agentic Awesome Skills 质量标准指南从技能提交到 Validated 徽章的完整验证流程 本篇指南聚焦 Agentic Awesome教程前端文档SwiftUI性能优化终极指南构建快速响应应用的8个核心技术SwiftUI性能优化终极指南构建快速响应应用的8个核心技术 SwiftUI作为Apple的声明式UI框架让开发者能够以更少的代码构建跨平台应用。然而随着终极React底层揭秘Under-the-hood-ReactJS图解三大核心机制终极React底层揭秘Under the hood ReactJS图解三大核心机制 Under the hood ReactJS是一个通过可视化流程图解全面解教程前端文档上一篇终极指南如何快速解决Steam Deck在Windows上的控制器映射问题下一篇如何像专业安全研究员一样高效使用FOFA Viewer从零到精通的实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表