React Reconciliation
在 React 项目中,应用程序在以下代码中开始渲染,该代码位于 index.js 文件中:
ReactDOM.render(<App />, document.getElementById('root'));
App 组件的实例由 ReactDOM.render 方法创建,并将调用相关组件的 render 方法。该方法中可能定义了多个组件。
结果就是,对于每个组件,都会调用相应的 render 方法。最终形成一个 HTML 元素的层级结构,并由上述方法的第二个参数插入到具有根 ID 的元素中。
例如,考虑以下组件,每个组件都在其文件中:
import React, {useEffect, useState} from "react";
import Button from "./Button";
const Counter = () => {
const[counter, setCounter] = useState(0);
useEffect(()=>{
console.log("Render Counter Component")
})
const btnClick = () => {
setCounter(counter+1)
}
return(
<div>
<span>{counter}</span>
<Button onClick={btnClick}></Button>
</div>
)
}
export default Counter
import React, { useEffect } from "react";
const Button = ({onClick}) => {
useEffect(()=>{
console.log("Render Button Component");
})
return(
<div>
<button onClick={()=>onClick()}>Click me</button>
</div>
)
}
export default Button
最后,在 App.js 组件内部,我们将有一个类似的结构:
function App() {
useEffect(()=>{
console.log("Render App Component")
})
return (
<div className="App">
<h1>Reconciliation Process</h1>
<Counter/>
</div>
);
}
在上述每个组件的 render 方法中,都写入了一个 console.log。运行应用后,可以在浏览器控制台中看到以下输出:
Render App Component
Render List Component
Render ActionButton Component
原因是,在应用运行时,React 会请求所有组件调用它们的 render 方法。当 HTML 内容显示在页面上时,应用会处于 reconciled 状态,此时显示的输出与组件的状态一致。
React 会等待更改(在大多数应用程序中,更改是由用户执行的),最终导致调用 setCounter 方法。setCounter 方法会更新组件的状态数据。但这会使组件变得陈旧。这意味着显示给用户的 HTML 内容将过时,并且仅一个事件就可能更改多个状态数据。这将导致所有更改的组件调用 render 方法。但是,由于更新 DOM 是一项成本高昂的操作,因此,React 会将先前的内容(缓存为虚拟 DOM)与新内容进行比较,以尽量减少 DOM 更新量。此过程称为协调。
为了更好地理解这个过程,我们将在 List 组件内部的 div 元素中添加一个 Id:
<div id="wrapper">
<span>{counter}</span>
<Button onClick={btnClick}></Button>
</div>
现在,在项目运行时,在浏览器控制台中运行以下代码:
document.getElementById("wrapper").classList.add("wrapper")
如您所见,下面的代码向 div 元素添加了一个消息类:
.wrapper{
border: 1px solid green;
padding: 2rem;
}
现在,当我们点击 Click me 按钮时,上述组件的内容将会改变。但 div 元素仍然保留着 message 类。正如前文所述,这是因为 React 会将 List 组件生成的内容与其自身的虚拟 DOM 进行比较。由于它们的 DOM 结构相同,因此 React 不会改变组件的输出结构,只会更新发生变化的部分。
现在我们将对上述组件进行如下更改:
import React, {useEffect, useState} from "react";
import Button from "./Button";
const Counter = () => {
const[counter, setCounter] = useState(0);
useEffect(()=>{
console.log("Render Counter Component")
})
const btnClick = () => {
setCounter(counter+1)
}
const wrapperElement = () => {
const wrapper =
<div id="wrapper">
<span>{counter}</span>
<Button onClick={btnClick}></Button>
</div>
return wrapper;
}
return(
wrapperElement()
)
}
export default Counter
在这里,如果我们再次将上述类添加到 div 元素,然后单击按钮,我们将看到 message 类将从 div 元素中删除。原因是 HTML 结构与虚拟 DOM 不同,并且 React 将使用新的更改重新渲染所需的组件。
注意:要查看 DOM 变化,您可以从开发者工具 > 更多工具 > 渲染中启用 Chrome 浏览器中的 Paint 闪烁,然后直观地查看变化。
鏂囩珷鏉ユ簮锛�https://dev.to/fpaghar/react-reconciliation-1gai