React初始渲染DOM操作与componentDidMount执行时机的技术疑问
先给出参考代码:
import React from "react"; import ReactDOM from "react-dom"; class CompA extends React.Component { componentDidMount() { console.log("a"); debugger; } render() { return <div>Component A</div>; } } class CompB extends React.Component { componentDidMount() { console.log("b"); } render() { return <div>Component B</div>; } } class CompC extends React.Component { componentDidMount() { console.log("c"); } render() { return <div>Component C</div>; } } class App extends React.Component { componentDidMount() { console.log("App"); } render() { return ( <div id="top-level-component"> <h1>Hello!</h1> <CompA /> <CompB /> <CompC /> </div> ); } } ReactDOM.render(<App />, document.getElementById("root"));
疑问1:初始渲染的DOM追加方式
没错,React在初始渲染时会一次性把完整的DOM树追加到#root元素中,绝对不会先插入#top-level-component,再逐个追加它的子元素节点。
React内部会先在内存里构建好整个虚拟DOM树,完成所有组件的render计算后,再把对应的真实DOM节点批量插入到页面中。这种批量操作是React性能优化的核心手段之一,能大幅减少频繁DOM操作带来的重排重绘开销——相当于先在“后台”把所有元素组装完毕,再一次性“交付”到真实DOM环境里。
疑问2:componentDidMount的执行顺序与DOM就绪状态
完全正确!componentDidMount的触发顺序是从最深层的子组件到父组件,所以控制台会依次输出 a b c App。
而且当CompA的componentDidMount执行时,整个App的DOM已经完全挂载到页面上了——包括它的兄弟组件CompB、CompC,以及父组件App的所有DOM元素。因为React的逻辑是先完成所有真实DOM的插入,才会开始按顺序调用组件的componentDidMount生命周期方法。这意味着你在CompA的componentDidMount里可以安全地查询到CompB、CompC的DOM节点(比如用document.querySelector),它们已经实实在在存在于页面中了。
疑问3:后续挂载场景中兄弟组件的DOM就绪性
这个结论同样成立!不管是初始渲染,还是后续通过条件渲染、列表更新等方式新增兄弟组件的场景,React都会遵循先完成所有相关DOM的批量插入,再按子到父的顺序调用componentDidMount的逻辑。
举个例子:如果你的App里有个按钮,点击后同时渲染CompD和CompE两个兄弟组件,那么当CompD的componentDidMount触发时,CompE的DOM已经挂载到页面上了,你可以放心地操作它的DOM节点。
内容的提问来源于stack exchange,提问作者Dimitris Karagiannis

