React教程【高级】:性能优化

UI 更新需要昂贵的 DOM 操作,而 React 内部使用几种巧妙的技术以便最小化 DOM 操作次数。对于大部分应用而言,使用 React 时无需专门优化就已拥有高性能的用户界面。尽管如此,你仍然有办法来加速你的 React 应用。

使用生产版本

当你需要对你的 React 应用进行 benchmark,或者遇到了性能问题,请确保你正在使用压缩后的生产版本。

React 默认包含了许多有用的警告信息。这些警告信息在开发过程中非常有帮助。然而这使得 React 变得更大且更慢,所以你需要确保部署时使用了生产版本。

如果你不能确定你的编译过程是否设置正确,你可以通过安装 Chrome 的 React 开发者工具 来检查。如果你浏览一个基于 React 生产版本的网站,图标背景会变成深色:

React教程【高级】:性能优化

如果你浏览一个基于 React 开发模式的网站,图标背景会变成红色:

React教程【高级】:性能优化

推荐你在开发应用时使用开发模式,而在为用户部署应用时使用生产模式。

你可以在下面看到几种为应用构建生产版本的操作说明。

Create React App

如果你的项目是通过 Create React App 构建的,运行:

npm run build

这段命令将在你的项目下的 build/ 目录中生成对应的生产版本。

注意只有在生产部署前才需要执行这个命令。正常开发使用 npm start 即可。

单文件构建

我们提供了可以在生产环境使用的单文件版 React 和 React DOM:

<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react/dom@16/umd/react/dom.production.min.js"></script><script src="https://unpkg.com/react/dom@16/umd/react/dom.production.min.js"></code></pre>



<p>注意只有以&nbsp;<code>.production.min.js</code>&nbsp;为结尾的 React 文件适用于生产。</p>



<h3 class="wp-block-heading" id="brunch"><span class="ez-toc-section" id="Brunch"></span><a href="https://react.docschina.org/docs/optimizing-performance.html#brunch"></a>Brunch<span class="ez-toc-section-end"></span></h3>



<p>通过安装&nbsp;<a href="https://github.com/brunch/terser-brunch" target="_blank" rel="noreferrer noopener"><code>terser-brunch</code></a>&nbsp;插件,来获得最高效的 Brunch 生产构建:</p>



<pre class="wp-block-code"><code># 如果你使用 npm
npm install --save-dev terser-brunch

# 如果你使用 Yarn
yarn add --dev terser-brunch</code></pre>



<p>接着,在&nbsp;<code>build</code>&nbsp;命令后添加&nbsp;<code>-p</code>&nbsp;参数,以创建生产构建:</p>



<pre class="wp-block-code"><code>brunch build -p</code></pre>



<p>请注意,你只需要在生产构建时这么做。你不需要在开发环境中使用&nbsp;<code>-p</code>&nbsp;参数或者应用这个插件,因为这会隐藏有用的 React 警告信息并使得构建速度变慢。</p>



<h3 class="wp-block-heading" id="browserify"><span class="ez-toc-section" id="Browserify"></span><a href="https://react.docschina.org/docs/optimizing-performance.html#browserify"></a>Browserify<span class="ez-toc-section-end"></span></h3>



<p>为了最高效的生产构建,需要安装一些插件:</p>



<pre class="wp-block-code"><code># 如果你使用 npm
npm install --save-dev envify terser uglifyify

# 如果你使用 Yarn
yarn add --dev envify terser uglifyify</code></pre>



<p>为了创建生产构建,确保你添加了以下转换器&nbsp;<strong>(顺序很重要)</strong></p>



<ul class="wp-block-list"><li><a href="https://github.com/hughsk/envify" target="_blank" rel="noreferrer noopener"><code>envify</code></a>&nbsp;转换器用于设置正确的环境变量。设置为全局 (<code>-g</code>)。</li><li><a href="https://github.com/hughsk/uglifyify" target="_blank" rel="noreferrer noopener"><code>uglifyify</code></a>&nbsp;转换器移除开发相关的引用代码。同样设置为全局 (<code>-g</code>)。</li><li>最后,将产物传给&nbsp;<a href="https://github.com/terser-js/terser" target="_blank" rel="noreferrer noopener"><code>terser</code></a>&nbsp;并进行压缩(<a href="https://github.com/hughsk/uglifyify#motivationusage" target="_blank" rel="noreferrer noopener">为什么要这么做?</a>)。</li></ul>



<p>举个例子:</p>



<pre class="wp-block-code"><code>browserify ./index.js \
  -g &#91; envify --NODE_ENV production ] \
  -g uglifyify \
  | terser --compress --mangle > ./bundle.js</code></pre>



<p>请注意,你只需要在生产构建时用到它。你不需要在开发环境应用这些插件,因为这会隐藏有用的 React 警告信息并使得构建速度变慢。</p>



<h3 class="wp-block-heading" id="rollup"><span class="ez-toc-section" id="Rollup"></span><a href="https://react.docschina.org/docs/optimizing-performance.html#rollup"></a>Rollup<span class="ez-toc-section-end"></span></h3>



<p>为了最高效的 Rollup 生产构建,需要安装一些插件:</p>



<pre class="wp-block-code"><code># 如果你使用 npm
npm install --save-dev rollup-plugin-commonjs rollup-plugin-replace rollup-plugin-terser

# 如果你使用 Yarn
yarn add --dev rollup-plugin-commonjs rollup-plugin-replace rollup-plugin-terser</code></pre>



<p>为了创建生产构建,确保你添加了以下插件&nbsp;<strong>(顺序很重要)</strong></p>



<ul class="wp-block-list"><li><a href="https://github.com/rollup/rollup-plugin-replace" target="_blank" rel="noreferrer noopener"><code>replace</code></a>&nbsp;插件确保环境被正确设置。</li><li><a href="https://github.com/rollup/rollup-plugin-commonjs" target="_blank" rel="noreferrer noopener"><code>commonjs</code></a>&nbsp;插件用于支持 CommonJS。</li><li><a href="https://github.com/TrySound/rollup-plugin-terser" target="_blank" rel="noreferrer noopener"><code>terser</code></a>&nbsp;插件用于压缩并生成最终的产物。</li></ul>



<pre class="wp-block-code"><code>plugins: &#91;
  // ...
  require('rollup-plugin-replace')({
    'process.env.NODE_ENV': JSON.stringify('production')
  }),
  require('rollup-plugin-commonjs')(),
  require('rollup-plugin-terser')(),
  // ...
]</code></pre>



<p><a href="https://gist.github.com/Rich-Harris/cb14f4bc0670c47d00d191565be36bf0" target="_blank" rel="noreferrer noopener">点击</a>查看完整的安装示例。</p>



<p>请注意,你只需要在生产构建时用到它。你不需要在开发中使用&nbsp;<code>terser</code>&nbsp;插件或者&nbsp;<code>replace</code>&nbsp;插件替换&nbsp;<code>'production'</code>&nbsp;变量,因为这会隐藏有用的 React 警告信息并使得构建速度变慢。</p>



<h3 class="wp-block-heading" id="webpack"><span class="ez-toc-section" id="webpack"></span><a href="https://react.docschina.org/docs/optimizing-performance.html#webpack"></a>webpack<span class="ez-toc-section-end"></span></h3>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p><strong>注意:</strong></p><p>如果你使用了 Create React App,请跟随上面的说明进行操作。<br>只有当你直接配置了 webpack 才需要参考以下内容。</p></blockquote>



<p>在生产模式下,Webpack v4+ 将默认对代码进行压缩:</p>



<pre class="wp-block-code"><code>const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
  mode: 'production',
  optimization: {
    minimizer: &#91;new TerserPlugin({ /* additional options here */ })],
  },
};</code></pre>



<p>你可以在&nbsp;<a href="https://webpack.js.org/guides/production/" target="_blank" rel="noreferrer noopener">webpack 文档</a>中了解更多内容。</p>



<p>请注意,你只需要在生产构建时用到它。你不需要在开发中使用&nbsp;<code>TerserPlugin</code>&nbsp;插件,因为这会隐藏有用的 React 警告信息并使得构建速度变慢。</p>



<h2 class="wp-block-heading" id="profiling-components-with-the-chrome-performance-tab"><span class="ez-toc-section" id="25E425BD25BF25E7259425A8_Chrome_Performance_25E625A0258725E725AD25BE25E52588258625E6259E259025E725BB258425E425BB25B6"></span><a href="https://react.docschina.org/docs/optimizing-performance.html#profiling-components-with-the-chrome-performance-tab"></a>使用 Chrome Performance 标签分析组件<span class="ez-toc-section-end"></span></h2>



<p><strong>开发</strong>模式下,你可以通过支持的浏览器可视化地了解组件是如何 挂载、更新以及卸载的。例如:<a href="https://react.docschina.org/static/64d522b74fb585f1abada9801f85fa9d/1ac66/react/perf-chrome-timeline.png" target="_blank" rel="noreferrer noopener"></a></p>



<p>在 Chrome 中进行如下操作:</p>



<ol class="wp-block-list"><li>临时<strong>禁用所有的 Chrome 扩展,尤其是 React 开发者工具</strong>。他们会严重干扰度量结果!</li><li>确保你是在 React 的开发模式下运行应用。</li><li>打开 Chrome 开发者工具的&nbsp;<strong><a href="https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/timeline-tool" target="_blank" rel="noreferrer noopener">Performance</a></strong>&nbsp;标签并按下&nbsp;<strong>Record</strong></li><li>对你想分析的行为进行复现。尽量在 20 秒内完成以避免 Chrome 卡住。</li><li>停止记录。</li><li>&nbsp;<strong>User Timing</strong>&nbsp;标签下会显示 React 归类好的事件。</li></ol>



<p>你可以查阅&nbsp;<a href="https://calibreapp.com/blog/react/performance-profiling-optimization" target="_blank" rel="noreferrer noopener">Ben Schwarz 的文章</a>以获取更详尽的指导。</p>



<p>需要注意的是<strong>在生产环境中组件会相对渲染得更快些</strong>。当然了,这能帮助你查看是否有不相关的组件被错误地更新,以及 UI 更新的深度和频率。</p>



<p>目前只有 Chrome、Edge 和 IE 支持该功能,但是我们使用的是标准的<a href="https://developer.mozilla.org/en-US/docs/Web/API/User_Timing_API" target="_blank" rel="noreferrer noopener">用户计时 API</a>。我们期待有更多浏览器能支持它。</p>



<h2 class="wp-block-heading" id="profiling-components-with-the-devtools-profiler"><span class="ez-toc-section" id="25E425BD25BF25E7259425A825E525BC258025E5258F259125E82580258525E525B725A525E5258525B725E425B825AD25E7259A258425E52588258625E6259E259025E5259925A825E525AF25B925E725BB258425E425BB25B625E825BF259B25E825A1258C25E52588258625E6259E2590"></span><a href="https://react.docschina.org/docs/optimizing-performance.html#profiling-components-with-the-devtools-profiler"></a>使用开发者工具中的分析器对组件进行分析<span class="ez-toc-section-end"></span></h2>



<p><code>react-dom</code> 16.5+ 和 <code>react-native</code> 0.57+ 加强了分析能力。在开发模式下,React 开发者工具会出现分析器标签。 你可以在《介绍 React 分析器》这篇博客中了解概述。 你也可以在 YouTube 上观看分析器的视频指导。</p>



<p>如果你还未安装 React 开发者工具,你可以在这里找到它们:</p>



<ul class="wp-block-list"><li><a href="https://chrome.google.com/webstore/detail/react/developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en" target="_blank" rel="noreferrer noopener">Chrome 浏览器扩展</a></li><li><a href="https://addons.mozilla.org/en-GB/firefox/addon/react/devtools/" target="_blank" rel="noreferrer noopener">Firefox 浏览器扩展</a></li><li><a href="https://www.npmjs.com/package/react/devtools" target="_blank" rel="noreferrer noopener">独立 Node 包</a></li></ul>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>注意</p><p><code>react-dom</code>&nbsp;的生产分析包也可以在&nbsp;<code>react-dom/profiling</code>&nbsp;中找到。 通过查阅&nbsp;<a href="https://fb.me/react/profiling" target="_blank" rel="noreferrer noopener">fb.me/react/profiling</a>&nbsp;来了解更多关于使用这个包的内容。</p></blockquote>



<h2 class="wp-block-heading" id="virtualize-long-lists"><span class="ez-toc-section" id="25E82599259A25E6258B259F25E5258C259625E9259525BF25E52588259725E825A125A8"></span><a href="https://react.docschina.org/docs/optimizing-performance.html#virtualize-long-lists"></a>虚拟化长列表<span class="ez-toc-section-end"></span></h2>



<p>如果你的应用渲染了长列表(上百甚至上千的数据),我们推荐使用“虚拟滚动”技术。这项技术会在有限的时间内仅渲染有限的内容,并奇迹般地降低重新渲染组件消耗的时间,以及创建 DOM 节点的数量。</p>



<p><a rel="noreferrer noopener" href="https://react/window.now.sh/" target="_blank">react-window</a> 和 <a rel="noreferrer noopener" href="https://bvaughn.github.io/react/virtualized/" target="_blank">react-virtualized</a> 是热门的虚拟滚动库。 它们提供了多种可复用的组件,用于展示列表、网格和表格数据。 如果你想要一些针对你的应用做定制优化,你也可以创建你自己的虚拟滚动组件,就像 Twitter 所做的。</p>



<h2 class="wp-block-heading" id="avoid-reconciliation"><span class="ez-toc-section" id="25E9258125BF25E52585258D25E825B0258325E52581259C"></span><a href="https://react.docschina.org/docs/optimizing-performance.html#avoid-reconciliation"></a>避免调停<span class="ez-toc-section-end"></span></h2>



<p>React 构建并维护了一套内部的 UI 渲染描述。它包含了来自你的组件返回的 React 元素。该描述使得 React 避免创建 DOM 节点以及没有必要的节点访问,因为 DOM 操作相对于 JavaScript 对象操作更慢。虽然有时候它被称为“虚拟 DOM”,但是它在 React Native 中拥有相同的工作原理。</p>



<p>当一个组件的 props 或 state 变更,React 会将最新返回的元素与之前渲染的元素进行对比,以此决定是否有必要更新真实的 DOM。当它们不相同时,React 会更新该 DOM。</p>



<p>即使 React 只更新改变了的 DOM 节点,重新渲染仍然花费了一些时间。在大部分情况下它并不是问题,不过如果它已经慢到让人注意了,你可以通过覆盖生命周期方法&nbsp;<code>shouldComponentUpdate</code>&nbsp;来进行提速。该方法会在重新渲染前被触发。其默认实现总是返回&nbsp;<code>true</code>,让 React 执行更新:</p>



<pre class="wp-block-code"><code>shouldComponentUpdate(nextProps, nextState) {
  return true;
}</code></pre>



<p>如果你知道在什么情况下你的组件不需要更新,你可以在&nbsp;<code>shouldComponentUpdate</code>&nbsp;中返回&nbsp;<code>false</code>&nbsp;来跳过整个渲染过程。其包括该组件的&nbsp;<code>render</code>&nbsp;调用以及之后的操作。</p>



<p>在大部分情况下,你可以继承 <a href="/react/2086.html#reactpurecomponent" data-type="URL" data-id="/react/2086.html#reactpurecomponent"><code>React.PureComponent</code></a> 以代替手写 <code>shouldComponentUpdate()</code>。它用当前与之前 props 和 state 的浅比较覆写了 <code>shouldComponentUpdate()</code> 的实现。</p>



<h2 class="wp-block-heading" id="shouldcomponentupdate-in-action"><span class="ez-toc-section" id="shouldComponentUpdate_25E7259A258425E425BD259C25E7259425A8"></span><a href="https://react.docschina.org/docs/optimizing-performance.html#shouldcomponentupdate-in-action"></a>shouldComponentUpdate 的作用<span class="ez-toc-section-end"></span></h2>



<p>这是一个组件的子树。每个节点中,<code>SCU</code>&nbsp;代表&nbsp;<code>shouldComponentUpdate</code>&nbsp;返回的值,而&nbsp;<code>vDOMEq</code>&nbsp;代表返回的 React 元素是否相同。最后,圆圈的颜色代表了该组件是否需要被调停。</p>



<figure class="wp-block-image size-large"><noscript><img decoding="async" width="555" height="371" src="/wp-content/uploads/2021/03/1.png" alt="React教程【高级】:性能优化" class="wp-image-2118" title=""/></noscript><img decoding="async" width="555" height="371" src="https://www.web176.com/wp-content/uploads/2022/05/loading.png" data-original="/wp-content/uploads/2021/03/1.png" alt="React教程【高级】:性能优化" class="wp-image-2118 j-lazy" title=""/></figure>



<p>节点 C2 的&nbsp;<code>shouldComponentUpdate</code>&nbsp;返回了&nbsp;<code>false</code>,React 因而不会去渲染 C2,也因此 C4 和 C5 的&nbsp;<code>shouldComponentUpdate</code>&nbsp;不会被调用到。</p>



<p>对于 C1 和 C3,<code>shouldComponentUpdate</code>&nbsp;返回了&nbsp;<code>true</code>,所以 React 需要继续向下查询子节点。这里 C6 的&nbsp;<code>shouldComponentUpdate</code>&nbsp;返回了&nbsp;<code>true</code>,同时由于渲染的元素与之前的不同使得 React 更新了该 DOM。</p>



<p>最后一个有趣的例子是 C8。React 需要渲染这个组件,但是由于其返回的 React 元素和之前渲染的相同,所以不需要更新 DOM。</p>



<p>显而易见,你看到 React 只改变了 C6 的 DOM。对于 C8,通过对比了渲染的 React 元素跳过了渲染。而对于 C2 的子节点和 C7,由于&nbsp;<code>shouldComponentUpdate</code>&nbsp;使得&nbsp;<code>render</code>&nbsp;并没有被调用。因此它们也不需要对比元素了。</p>



<h2 class="wp-block-heading" id="examples"><span class="ez-toc-section" id="25E725A425BA25E425BE258B"></span><a href="https://react.docschina.org/docs/optimizing-performance.html#examples"></a>示例<span class="ez-toc-section-end"></span></h2>



<p>如果你的组件只有当 <code>props.color</code> 或者 <code>state.count</code> 的值改变才需要更新时,你可以使用 <code>shouldComponentUpdate</code> 来进行检查:</p>



<pre class="wp-block-code"><code>class CounterButton extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: 1};
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (this.props.color !== nextProps.color) {
      return true;
    }
    if (this.state.count !== nextState.count) {
      return true;
    }
    return false;
  }

  render() {
    return (
      &lt;button
        color={this.props.color}
        onClick={() => this.setState(state => ({count: state.count + 1}))}>
        Count: {this.state.count}
      &lt;/button>
    );
  }
}</code></pre>



<p>在这段代码中,<code>shouldComponentUpdate</code> 仅检查了 <code>props.color</code> 或 <code>state.count</code> 是否改变。如果这些值没有改变,那么这个组件不会更新。如果你的组件更复杂一些,你可以使用类似“浅比较”的模式来检查 <code>props</code> 和 <code>state</code> 中所有的字段,以此来决定是否组件需要更新。React 已经提供了一位好帮手来帮你实现这种常见的模式 - 你只要继承 <code>React.PureComponent</code> 就行了。所以这段代码可以改成以下这种更简洁的形式:</p>



<pre class="wp-block-code"><code>class CounterButton extends React.PureComponent {
  constructor(props) {
    super(props);
    this.state = {count: 1};
  }

  render() {
    return (
      &lt;button
        color={this.props.color}
        onClick={() => this.setState(state => ({count: state.count + 1}))}>
        Count: {this.state.count}
      &lt;/button>
    );
  }
}</code></pre>



<p>大部分情况下,你可以使用 <code>React.PureComponent</code> 来代替手写 <code>shouldComponentUpdate</code>。但它只进行浅比较,所以当 props 或者 state 某种程度是可变的话,浅比较会有遗漏,那你就不能使用它了。当数据结构很复杂时,情况会变得麻烦。例如,你想要一个 <code>ListOfWords</code> 组件来渲染一组用逗号分开的单词。它有一个叫做 <code>WordAdder</code> 的父组件,该组件允许你点击一个按钮来添加一个单词到列表中。以下代码<em>并不</em>正确:</p>



<pre class="wp-block-code"><code>class ListOfWords extends React.PureComponent {
  render() {
    return &lt;div>{this.props.words.join(',')}&lt;/div>;
  }
}

class WordAdder extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      words: &#91;'marklar']
    };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    // 这部分代码很糟,而且还有 bug
    const words = this.state.words;
    words.push('marklar');
    this.setState({words: words});
  }

  render() {
    return (
      &lt;div>
        &lt;button onClick={this.handleClick} />
        &lt;ListOfWords words={this.state.words} />
      &lt;/div>
    );
  }
}</code></pre>



<p>问题在于&nbsp;<code>PureComponent</code>&nbsp;仅仅会对新老&nbsp;<code>this.props.words</code>&nbsp;的值进行简单的对比。由于代码中&nbsp;<code>WordAdder</code>&nbsp;&nbsp;<code>handleClick</code>&nbsp;方法改变了同一个&nbsp;<code>words</code>&nbsp;数组,使得新老&nbsp;<code>this.props.words</code>&nbsp;比较的其实还是同一个数组。即便实际上数组中的单词已经变了,但是比较结果是相同的。可以看到,即便多了新的单词需要被渲染,&nbsp;<code>ListOfWords</code>&nbsp;却并没有被更新。</p>



<h2 class="wp-block-heading" id="the-power-of-not-mutating-data"><span class="ez-toc-section" id="25E425B8258D25E5258F25AF25E5258F259825E6259525B025E6258D25AE25E7259A258425E5258A259B25E92587258F"></span><a href="https://react.docschina.org/docs/optimizing-performance.html#the-power-of-not-mutating-data"></a>不可变数据的力量<span class="ez-toc-section-end"></span></h2>



<p>避免该问题最简单的方式是避免更改你正用于 props 或 state 的值。例如,上面&nbsp;<code>handleClick</code>&nbsp;方法可以用&nbsp;<code>concat</code>&nbsp;重写:</p>



<pre class="wp-block-code"><code>handleClick() {
  this.setState(state => ({
    words: state.words.concat(&#91;'marklar'])
  }));
}</code></pre>



<p>ES6 数组支持<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator" target="_blank" rel="noreferrer noopener">扩展运算符</a>,这让代码写起来更方便了。如果你在使用 Create React App,该语法已经默认支持了。</p>



<pre class="wp-block-code"><code>handleClick() {
  this.setState(state => ({
    words: &#91;...state.words, 'marklar'],
  }));
};</code></pre>



<p>你可以用类似的方式改写代码来避免可变对象的产生。例如,我们有一个叫做&nbsp;<code>colormap</code>&nbsp;的对象。我们希望写一个方法来将&nbsp;<code>colormap.right</code>&nbsp;设置为&nbsp;<code>'blue'</code>。我们可以这么写:</p>



<pre class="wp-block-code"><code>function updateColorMap(colormap) {
  colormap.right = 'blue';
}</code></pre>



<p>为了不改变原本的对象,我们可以使用&nbsp;<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign" target="_blank" rel="noreferrer noopener">Object.assign</a>&nbsp;方法:</p>



<pre class="wp-block-code"><code>function updateColorMap(colormap) {
  return Object.assign({}, colormap, {right: 'blue'});
}</code></pre>



<p>现在&nbsp;<code>updateColorMap</code>&nbsp;返回了一个新的对象,而不是修改老对象。<code>Object.assign</code>&nbsp;是 ES6 的方法,需要 polyfill。</p>



<p>这里有一个 JavaScript 的提案,旨在添加对象扩展属性以使得更新不可变对象变得更方便:</p>



<pre class="wp-block-code"><code>function updateColorMap(colormap) {
  return {...colormap, right: 'blue'};
}</code></pre>



<p>如果你在使用 Create React App,<code>Object.assign</code>&nbsp;以及对象扩展运算符已经默认支持了。</p>



<p>当处理深层嵌套对象时,以 immutable (不可变)的方式更新它们令人费解。如遇到此类问题,请参阅&nbsp;<a href="https://github.com/mweststrate/immer" target="_blank" rel="noreferrer noopener">Immer</a>&nbsp;&nbsp;<a href="https://github.com/kolodny/immutability-helper" target="_blank" rel="noreferrer noopener">immutability-helper</a>。这些库会帮助你编写高可读性的代码,且不会失去 immutability (不可变性)带来的好处。</p>
<div class="entry-readmore"><div class="entry-readmore-btn"></div></div>                                                        <div class="entry-copyright"><p>作者:terry,如若转载,请注明出处:https://www.web176.com/react/2117.html</p></div>                        </div>

                        <div class="entry-tag"><span class="entry-specials"><a href="https://www.web176.com/special/react" rel="tag">React教程</a></span></div>
                        <div class="entry-action">
                            <div class="btn-zan" data-id="2117"><i class="wpcom-icon wi"><svg aria-hidden="true"><use xlink:href="#wi-thumb-up-fill"></use></svg></i><span class="entry-action-num">(0)</span></div>
                                                            <div class="btn-dashang">
                                    <i class="wpcom-icon wi"><svg aria-hidden="true"><use xlink:href="#wi-cny-circle-fill"></use></svg></i> 打赏                                    <span class="dashang-img dashang-img2">
                                                                                    <span>
                                                <img src="/wp-content/uploads/2022/05/alipay.jpg" alt="支付宝"/>
                                                    支付宝                                            </span>
                                                                                                                            <span>
                                                <img src="//www.web176.com/wp-content/uploads/2022/05/tx.jpg" alt="微信"/>
                                                    微信                                            </span>
                                                                            </span>
                                </div>
                                                    </div>

                        <div class="entry-bar">
                            <div class="entry-bar-inner">
                                                                    <div class="entry-bar-author">
                                                                                <a data-user="1" target="_blank" href="https://www.web176.com/usercenter/terry" class="avatar j-user-card">
                                            <img alt='terry' src='//g.izt6.com/avatar/4723f29c633fe2ce2238c45b5ba23adb?s=60&#038;d=mm&#038;r=g' srcset='//g.izt6.com/avatar/4723f29c633fe2ce2238c45b5ba23adb?s=120&#038;d=mm&#038;r=g 2x' class='avatar avatar-60 photo' height='60' width='60' decoding='async'/><span class="author-name">terry</span>                                        </a>
                                    </div>
                                                                <div class="entry-bar-info">
                                    <div class="info-item meta">
                                                                                    <a class="meta-item j-heart" href="javascript:;" data-id="2117"><i class="wpcom-icon wi"><svg aria-hidden="true"><use xlink:href="#wi-star"></use></svg></i> <span class="data">0</span></a>                                        <a class="meta-item" href="#comments"><i class="wpcom-icon wi"><svg aria-hidden="true"><use xlink:href="#wi-comment"></use></svg></i> <span class="data">0</span></a>                                                                            </div>
                                    <div class="info-item share">
                                        <a class="meta-item mobile j-mobile-share" href="javascript:;" data-id="2117" data-qrcode="https://www.web176.com/react/2117.html"><i class="wpcom-icon wi"><svg aria-hidden="true"><use xlink:href="#wi-share"></use></svg></i> 生成海报</a>
                                                                                    <a class="meta-item wechat" data-share="wechat" target="_blank" rel="nofollow" href="#">
                                                <i class="wpcom-icon wi"><svg aria-hidden="true"><use xlink:href="#wi-wechat"></use></svg></i>                                            </a>
                                                                                    <a class="meta-item weibo" data-share="weibo" target="_blank" rel="nofollow" href="#">
                                                <i class="wpcom-icon wi"><svg aria-hidden="true"><use xlink:href="#wi-weibo"></use></svg></i>                                            </a>
                                                                                    <a class="meta-item qq" data-share="qq" target="_blank" rel="nofollow" href="#">
                                                <i class="wpcom-icon wi"><svg aria-hidden="true"><use xlink:href="#wi-qq"></use></svg></i>                                            </a>
                                                                                    <a class="meta-item qzone" data-share="qzone" target="_blank" rel="nofollow" href="#">
                                                <i class="wpcom-icon wi"><svg aria-hidden="true"><use xlink:href="#wi-qzone"></use></svg></i>                                            </a>
                                                                            </div>
                                    <div class="info-item act">
                                        <a href="javascript:;" id="j-reading"><i class="wpcom-icon wi"><svg aria-hidden="true"><use xlink:href="#wi-article"></use></svg></i></a>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                                            <div class="entry-page">
                    <div class="entry-page-prev" style="background-image: url('https://www.web176.com/wp-content/uploads/2021/02/react-480x300.jpg');">
                <a href="https://www.web176.com/react/2114.html" title="React教程【高级】:Portals" rel="prev">
                    <span>React教程【高级】:Portals</span>
                </a>
                <div class="entry-page-info">
                    <span class="pull-left"><i class="wpcom-icon wi"><svg aria-hidden="true"><use xlink:href="#wi-arrow-left-double"></use></svg></i> 上一篇</span>
                    <span class="pull-right">2021年3月24日 下午3:34</span>
                </div>
            </div>
                            <div class="entry-page-next" style="background-image: url('https://www.web176.com/wp-content/uploads/2021/02/react-480x300.jpg');">
                <a href="https://www.web176.com/react/2124.html" title="React教程【高级】:深入JSX" rel="next">
                    <span>React教程【高级】:深入JSX</span>
                </a>
                <div class="entry-page-info">
                    <span class="pull-right">下一篇 <i class="wpcom-icon wi"><svg aria-hidden="true"><use xlink:href="#wi-arrow-right-double"></use></svg></i></span>
                    <span class="pull-left">2021年3月29日 下午1:59</span>
                </div>
            </div>
            </div>
                    <div class="wpcom_myimg_wrap __single_2"><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5895294098898543"
     crossorigin="anonymous">

相关推荐

发表回复

登录后才能评论