ReactJS重新渲染的规则只是:如果组件的状态发生变化,那么整个子树都会重新渲染吗?

问题描述 投票:0回答:4

首先,所谓“重新渲染”,这里的意思是要么

  1. 调用任何类组件的
    render()
    方法,OR
  2. 调用函数组件的函数。

我们将实际 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 闪烁,而只是针对计数器编号闪烁。

这就是它的工作原理吗?

  1. 每当组件的任何状态发生更改时,该组件和整个子树都将“重新渲染”。
  2. 但是 ReactJS 会将使用这些 JSX 构建的“虚拟 DOM”的内容与实际 DOM 的内容进行“协调”,如果内容不同,则“刷新实际 DOM”。

但是话虽如此,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>

reactjs react-state virtual-dom
4个回答
1
投票

渲染后,React 获取视图应该是什么样子的 json,计算变化,然后更改实际的 dom。因此,即使 A 被重新渲染,输出 json 也将与虚拟 dom 相同,因此 React 不会触及 dom


1
投票

这是为了回答您问题的

WHY
部分 -

正如此链接中所解释的 - https://gist.github.com/paulirish/5d52fb081b3570c81e3a,很多事情都可以触发 DOM 的回流。不仅仅是设置,甚至访问 DOM 元素属性也会导致 DOM 的回流,这是一个非常昂贵的过程。

因此,如果 React 开始与实际 DOM 进行比较,那么它将降低而不是提高渲染性能。这就是为什么 React 会将更改与之前的副本进行比较,并在需要时将更改更新到实际 DOM。


0
投票

阅读了关于 Reconciliation 的 React 文档后,似乎有一个简化的思考方式:

  1. 每当提供给组件
    COMPO1
    的props或者该组件的状态发生变化时,就会调用所有类组件和
    COMPO1
    下的函数组件的render(),形成一棵虚拟DOM树,以及整个子树与之前的子树进行比较——不是与实际的 DOM 比较,而是与之前的虚拟 DOM 子树进行比较
  2. 比较节点是否不同,并对所有子节点递归执行此操作。
  3. 只有组件中不同的最小子树才会导致实际 DOM 的内容刷新——这意味着如果节点 A 有子节点 B 和 C,并且 B 保持不变,而节点 C 变得不同,则只有 C 会发生变化导致实际 DOM 的该部分被刷新。 (当然,如果节点 C 具有节点 D,并且 E 和 D 保持不变,而 E 不同,则只有 E 刷新为实际 DOM)。

0
投票

@nonopolity 所有答案都集中在回答你问题的一部分,即为什么 React 使用虚拟 dom。但是问题是,实际 DOM 中的文本更改为“hi”后,为什么在单击按钮 3 秒后,React 没有将文本更改回原始状态呢? React内存中的虚拟dom本来就有原始文本,为什么重新渲染时没有改变呢?

© www.soinside.com 2019 - 2024. All rights reserved.