首先,所谓“重新渲染”,这里的意思是要么
render()
方法,OR我们将实际 DOM 中发生更改的元素称为“刷新”,以区别于“重新渲染”。
“重新渲染”的规则是不是这么简单:
当组件的任何状态发生更改时,该组件以及该组件以下的所有子树都会重新渲染
就这样? 例如:
function A() {
console.log("Component A re-render");
return <div>Component A says Hello World</div>;
}
function App() {
const [counter, setCounter] = React.useState(0);
console.log("Component App re-render");
function increaseCount() {
setCounter(c => c + 1);
}
return (
<div>
{counter}
<button onClick={increaseCount} >Increment</button>
<A />
</div>
)
}
ReactDOM.render(<App />, document.querySelector("#root"));
<script src="https://unpkg.com/[email protected]/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>
组件 A 非常简单:它甚至不需要任何 props,只是输出静态文本,但它仍然每次都会被调用(通过查看
console.log()
输出)。
但即使是“重新渲染”,实际的 DOM 元素并没有“刷新”,从 Google Chrome 的 Inspect Element 中可以看出,DOM 元素不会针对组件 A 闪烁,而只是针对计数器编号闪烁。
这就是它的工作原理吗?
但是话虽如此,ReactJS 似乎并没有真正与实际 DOM 协调,但可能与“以前的虚拟 DOM”协调。为什么? 因为如果我在 3 秒后使用
setTimeout()
将组件 A 的实际 DOM 更改为其他内容,然后单击按钮,ReactJS 不会将组件 A 的内容更改回“Hello World”。示例:
function A() {
console.log("Component A re-render");
return <div id="foo">Component A says Hello World</div>;
}
function App() {
const [counter, setCounter] = React.useState(0);
console.log("Component App re-render");
function increaseCount() {
setCounter(c => c + 1);
}
return (
<div>
{counter}
<button onClick={increaseCount} >Increment</button>
<A />
</div>
)
}
ReactDOM.render(<App />, document.querySelector("#root"));
setTimeout(function() {
document.querySelector("#foo").innerText = "hi"
}, 3000);
<script src="https://unpkg.com/[email protected]/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>
渲染后,React 获取视图应该是什么样子的 json,计算变化,然后更改实际的 dom。因此,即使 A 被重新渲染,输出 json 也将与虚拟 dom 相同,因此 React 不会触及 dom
这是为了回答您问题的
WHY
部分 -
正如此链接中所解释的 - https://gist.github.com/paulirish/5d52fb081b3570c81e3a,很多事情都可以触发 DOM 的回流。不仅仅是设置,甚至访问 DOM 元素属性也会导致 DOM 的回流,这是一个非常昂贵的过程。
因此,如果 React 开始与实际 DOM 进行比较,那么它将降低而不是提高渲染性能。这就是为什么 React 会将更改与之前的副本进行比较,并在需要时将更改更新到实际 DOM。
阅读了关于 Reconciliation 的 React 文档后,似乎有一个简化的思考方式:
COMPO1
的props或者该组件的状态发生变化时,就会调用所有类组件和COMPO1
下的函数组件的render(),形成一棵虚拟DOM树,以及整个子树与之前的子树进行比较——不是与实际的 DOM 比较,而是与之前的虚拟 DOM 子树进行比较@nonopolity 所有答案都集中在回答你问题的一部分,即为什么 React 使用虚拟 dom。但是问题是,实际 DOM 中的文本更改为“hi”后,为什么在单击按钮 3 秒后,React 没有将文本更改回原始状态呢? React内存中的虚拟dom本来就有原始文本,为什么重新渲染时没有改变呢?