diff --git a/content/docs/optimizing-performance.md b/content/docs/optimizing-performance.md index e42bad7b76..85e502a41d 100644 --- a/content/docs/optimizing-performance.md +++ b/content/docs/optimizing-performance.md @@ -6,88 +6,88 @@ redirect_from: - "docs/advanced-performance.html" --- -Internally, React uses several clever techniques to minimize the number of costly DOM operations required to update the UI. For many applications, using React will lead to a fast user interface without doing much work to specifically optimize for performance. Nevertheless, there are several ways you can speed up your React application. +UI 更新需要昂贵的 DOM 操作,而 React 内部使用几种巧妙的技术以便最小化 DOM 操作次数。对于大部分应用而言,使用 React 时无需专门优化就已拥有高性能的用户界面。尽管如此,你仍然有办法来加速你的 React 应用。 -## Use the Production Build {#use-the-production-build} +## 使用生产版本 {#use-the-production-build} -If you're benchmarking or experiencing performance problems in your React apps, make sure you're testing with the minified production build. +当你需要对你的 React 应用进行 benchmark,或者遇到了性能问题,请确保你正在使用压缩后的生产版本。 -By default, React includes many helpful warnings. These warnings are very useful in development. However, they make React larger and slower so you should make sure to use the production version when you deploy the app. +React 默认包含了许多有用的警告信息。这些警告信息在开发过程中非常有帮助。然而这使得 React 变得更大且更慢,所以你需要确保部署时使用了生产版本。 -If you aren't sure whether your build process is set up correctly, you can check it by installing [React Developer Tools for Chrome](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi). If you visit a site with React in production mode, the icon will have a dark background: +如果你不能确定你的编译过程是否设置正确,你可以通过安装 [Chrome 的 React 开发者工具](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi) 来检查。如果你浏览一个基于 React 生产版本的网站,图标背景会变成深色: React DevTools on a website with production version of React -If you visit a site with React in development mode, the icon will have a red background: +如果你浏览一个基于 React 开发模式的网站,图标背景会变成红色: React DevTools on a website with development version of React -It is expected that you use the development mode when working on your app, and the production mode when deploying your app to the users. +推荐你在开发应用时使用开发模式,而在为用户部署应用时使用生产模式。 -You can find instructions for building your app for production below. +你可以在下面看到几种为应用构建生产版本的操作说明。 ### Create React App {#create-react-app} -If your project is built with [Create React App](https://github.com/facebookincubator/create-react-app), run: +如果你的项目是通过 [Create React App](https://github.com/facebookincubator/create-react-app) 构建的,运行: ``` npm run build ``` -This will create a production build of your app in the `build/` folder of your project. +这段命令将在你的项目下的 `build/` 目录中生成对应的生产版本。 -Remember that this is only necessary before deploying to production. For normal development, use `npm start`. +注意只有在生产部署前才需要执行这个命令。正常开发使用 `npm start` 即可。 -### Single-File Builds {#single-file-builds} +### 单文件构建 {#single-file-builds} -We offer production-ready versions of React and React DOM as single files: +我们提供了可以在生产环境使用的单文件版 React 和 React DOM: ```html ``` -Remember that only React files ending with `.production.min.js` are suitable for production. +注意只有以 `.production.min.js` 为结尾的 React 文件适用于生产。 ### Brunch {#brunch} -For the most efficient Brunch production build, install the [`uglify-js-brunch`](https://github.com/brunch/uglify-js-brunch) plugin: +通过安装 [`uglify-js-brunch`](https://github.com/brunch/uglify-js-brunch) 插件,来获得最高效的 Brunch 生产构建: ``` -# If you use npm +# 如果你使用 npm npm install --save-dev uglify-js-brunch -# If you use Yarn +# 如果你使用 Yarn yarn add --dev uglify-js-brunch ``` -Then, to create a production build, add the `-p` flag to the `build` command: +接着,在 `build` 命令后添加 `-p` 参数,以创建生产构建: ``` brunch build -p ``` -Remember that you only need to do this for production builds. You shouldn't pass the `-p` flag or apply this plugin in development, because it will hide useful React warnings and make the builds much slower. +请注意,你只需要在生产构建时这么做。你不需要在开发环境中使用 `-p` 参数或者应用这个插件,因为这会隐藏有用的 React 警告信息并使得构建速度变慢。 ### Browserify {#browserify} -For the most efficient Browserify production build, install a few plugins: +为了最高效的生产构建,需要安装一些插件: ``` -# If you use npm +# 如果你使用 npm npm install --save-dev envify uglify-js uglifyify -# If you use Yarn +# 如果你使用 Yarn yarn add --dev envify uglify-js uglifyify ``` -To create a production build, make sure that you add these transforms **(the order matters)**: +为了创建生产构建,确保你添加了以下转换器 **(顺序很重要)**: -* The [`envify`](https://github.com/hughsk/envify) transform ensures the right build environment is set. Make it global (`-g`). -* The [`uglifyify`](https://github.com/hughsk/uglifyify) transform removes development imports. Make it global too (`-g`). -* Finally, the resulting bundle is piped to [`uglify-js`](https://github.com/mishoo/UglifyJS2) for mangling ([read why](https://github.com/hughsk/uglifyify#motivationusage)). +* [`envify`](https://github.com/hughsk/envify) 转换器用于设置正确的环境变量。设置为全局 (`-g`)。 +* [`uglifyify`](https://github.com/hughsk/uglifyify) 转换器移除开发相关的引用代码。同样设置为全局 (`-g`)。 +* 最后,将产物传给 [`uglify-js`](https://github.com/mishoo/UglifyJS2) 用以压缩([为什么要这么做?](https://github.com/hughsk/uglifyify#motivationusage))。 -For example: +举个例子: ``` browserify ./index.js \ @@ -96,30 +96,30 @@ browserify ./index.js \ | uglifyjs --compress --mangle > ./bundle.js ``` ->**Note:** +>**注意:** > ->The package name is `uglify-js`, but the binary it provides is called `uglifyjs`.
->This is not a typo. +>虽然这个包的名字叫做 `uglify-js`,但是执行文件叫做 `uglifyjs`。
+>这不是拼写错误。 -Remember that you only need to do this for production builds. You shouldn't apply these plugins in development because they will hide useful React warnings, and make the builds much slower. +请注意,你只需要在生产构建时用到它。你不需要在开发环境应用这些插件,因为这会隐藏有用的 React 警告信息并使得构建速度变慢。 ### Rollup {#rollup} -For the most efficient Rollup production build, install a few plugins: +为了最高效的 Rollup 生产构建,需要安装一些插件: ``` -# If you use npm +# 如果你使用 npm npm install --save-dev rollup-plugin-commonjs rollup-plugin-replace rollup-plugin-uglify -# If you use Yarn +# 如果你使用 Yarn yarn add --dev rollup-plugin-commonjs rollup-plugin-replace rollup-plugin-uglify ``` -To create a production build, make sure that you add these plugins **(the order matters)**: +为了创建生产构建,确保你添加了以下插件 **(顺序很重要)**: -* The [`replace`](https://github.com/rollup/rollup-plugin-replace) plugin ensures the right build environment is set. -* The [`commonjs`](https://github.com/rollup/rollup-plugin-commonjs) plugin provides support for CommonJS in Rollup. -* The [`uglify`](https://github.com/TrySound/rollup-plugin-uglify) plugin compresses and mangles the final bundle. +* [`replace`](https://github.com/rollup/rollup-plugin-replace) 插件确保环境被正确设置。 +* [`commonjs`](https://github.com/rollup/rollup-plugin-commonjs) 插件用于支持 CommonJS。 +* [`uglify`](https://github.com/TrySound/rollup-plugin-uglify) 插件用于压缩并生成最终的产物。 ```js plugins: [ @@ -133,18 +133,18 @@ plugins: [ ] ``` -For a complete setup example [see this gist](https://gist.github.com/Rich-Harris/cb14f4bc0670c47d00d191565be36bf0). +[点击](https://gist.github.com/Rich-Harris/cb14f4bc0670c47d00d191565be36bf0)查看完整的安装示例。 -Remember that you only need to do this for production builds. You shouldn't apply the `uglify` plugin or the `replace` plugin with `'production'` value in development because they will hide useful React warnings, and make the builds much slower. +请注意,你只需要在生产构建时用到它。你不需要在开发中使用 `uglify` 插件或者 `replace` 插件替换 `'production'` 变量,因为这会隐藏有用的 React 警告信息并使得构建速度变慢。 ### webpack {#webpack} ->**Note:** +>**注意:** > ->If you're using Create React App, please follow [the instructions above](#create-react-app).
->This section is only relevant if you configure webpack directly. +>如果你使用了 Create React App,请跟随[上面的说明](#create-react-app)进行操作。
+>只有当你直接配置了 webpack 才需要参考以下内容。 -For the most efficient webpack production build, make sure to include these plugins in your production configuration: +为了最高效的 webpack 生产构建,确保在你的生产配置中包含这些插件: ```js new webpack.DefinePlugin({ @@ -153,84 +153,86 @@ new webpack.DefinePlugin({ new webpack.optimize.UglifyJsPlugin() ``` -You can learn more about this in [webpack documentation](https://webpack.js.org/guides/production-build/). +你可以在 [webpack 文档](https://webpack.js.org/guides/production-build/)中了解更多内容。 -Remember that you only need to do this for production builds. You shouldn't apply `UglifyJsPlugin` or `DefinePlugin` with `'production'` value in development because they will hide useful React warnings, and make the builds much slower. +请注意,你只需要在生产构建时用到它。你不需要在开发中使用 `UglifyJsPlugin` 插件或者 `DefinePlugin` 插件设置 `'production'` 变量,因为这会隐藏有用的 React 警告信息并使得构建速度变慢。 -## Profiling Components with the Chrome Performance Tab {#profiling-components-with-the-chrome-performance-tab} +## 使用 Chrome Performance 标签分析组件 {#profiling-components-with-the-chrome-performance-tab} -In the **development** mode, you can visualize how components mount, update, and unmount, using the performance tools in supported browsers. For example: +在**开发**模式下,你可以通过支持的浏览器可视化地了解组件是如何 挂载、更新以及卸载的。例如: -
React components in Chrome timeline
+
在 Chrome 时间线中的 React 组件
-To do this in Chrome: +在 Chrome 中进行如下操作: -1. Temporarily **disable all Chrome extensions, especially React DevTools**. They can significantly skew the results! +1. 临时**禁用所有的 Chrome 扩展,尤其是 React 开发者工具**。他们会严重干扰度量结果! -2. Make sure you're running the application in the development mode. +2. 确保你是在 React 的开发模式下运行应用。 -3. Open the Chrome DevTools **[Performance](https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/timeline-tool)** tab and press **Record**. +3. 打开 Chrome 开发者工具的 **[Performance](https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/timeline-tool)** 标签并按下 **Record**。 -4. Perform the actions you want to profile. Don't record more than 20 seconds or Chrome might hang. +4. 对你想分析的行为进行复现。尽量在 20 秒内完成以避免 Chrome 卡住。 -5. Stop recording. +5. 停止记录。 -6. React events will be grouped under the **User Timing** label. +6. 在 **User Timing** 标签下会显示 React 归类好的事件。 -For a more detailed walkthrough, check out [this article by Ben Schwarz](https://calibreapp.com/blog/2017-11-28-debugging-react/). +你可以查阅[这篇文章](https://calibreapp.com/blog/2017-11-28-debugging-react/)以获取更详尽的指导。 -Note that **the numbers are relative so components will render faster in production**. Still, this should help you realize when unrelated UI gets updated by mistake, and how deep and how often your UI updates occur. +需要注意的是**在生产环境中组件会相对渲染得更快些**。当然了,这能帮助你查看是否有不相关的组件被错误地更新,以及 UI 更新的深度和频率。 -Currently Chrome, Edge, and IE are the only browsers supporting this feature, but we use the standard [User Timing API](https://developer.mozilla.org/en-US/docs/Web/API/User_Timing_API) so we expect more browsers to add support for it. +目前只有 Chrome、Edge 和 IE 支持该功能,但是我们使用的是标准的[用户计时 API](https://developer.mozilla.org/en-US/docs/Web/API/User_Timing_API)。我们期待有更多浏览器能支持它。 -## Profiling Components with the DevTools Profiler {#profiling-components-with-the-devtools-profiler} +## 使用开发者工具中的分析器对组件进行分析 {#profiling-components-with-the-devtools-profiler} -`react-dom` 16.5+ and `react-native` 0.57+ provide enhanced profiling capabilities in DEV mode with the React DevTools Profiler. -An overview of the Profiler can be found in the blog post ["Introducing the React Profiler"](/blog/2018/09/10/introducing-the-react-profiler.html). -A video walkthrough of the profiler is also [available on YouTube](https://www.youtube.com/watch?v=nySib7ipZdk). +`react-dom` 16.5+ 和 `react-native` 0.57+ 加强了分析能力。在开发模式下,React 开发者工具会出现分析器标签。 +你可以在[《介绍 React 分析器》](/blog/2018/09/10/introducing-the-react-profiler.html)这篇博客中了解概述。 +你也可以[在 YouTube 上](https://www.youtube.com/watch?v=nySib7ipZdk)观看分析器的视频指导。 -If you haven't yet installed the React DevTools, you can find them here: +如果你还未安装 React 开发者工具,你可以在这里找到它们: -- [Chrome Browser Extension](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en) -- [Firefox Browser Extension](https://addons.mozilla.org/en-GB/firefox/addon/react-devtools/) -- [Standalone Node Package](https://www.npmjs.com/package/react-devtools) +- [Chrome 浏览器扩展](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en) +- [Firefox 浏览器扩展](https://addons.mozilla.org/en-GB/firefox/addon/react-devtools/) +- [独立 Node 包](https://www.npmjs.com/package/react-devtools) -> Note +> 注意 > -> A production profiling bundle of `react-dom` is also available as `react-dom/profiling`. -> Read more about how to use this bundle at [fb.me/react-profiling](https://fb.me/react-profiling) +>`react-dom` 的生产分析包也可以在 `react-dom/profiling` 中找到。 +>通过查阅 [fb.me/react-profiling](https://fb.me/react-profiling) 来了解更多关于使用这个包的内容。 -## Virtualize Long Lists {#virtualize-long-lists} +## 虚拟化长列表 {#virtualize-long-lists} -If your application renders long lists of data (hundreds or thousands of rows), we recommended using a technique known as "windowing". This technique only renders a small subset of your rows at any given time, and can dramatically reduce the time it takes to re-render the components as well as the number of DOM nodes created. +如果你的应用渲染了长列表(上百甚至上千的数据),我们推荐使用“虚拟滚动”技术。这项技术会在有限的时间内仅渲染有限的内容,并奇迹般地降低重新渲染组件消耗的时间,以及创建 DOM 节点的数量。 -[react-window](https://react-window.now.sh/) and [react-virtualized](https://bvaughn.github.io/react-virtualized/) are popular windowing libraries. They provide several reusable components for displaying lists, grids, and tabular data. You can also create your own windowing component, like [Twitter did](https://medium.com/@paularmstrong/twitter-lite-and-high-performance-react-progressive-web-apps-at-scale-d28a00e780a3), if you want something more tailored to your application's specific use case. +[react-window](https://react-window.now.sh/) 和 [react-virtualized](https://bvaughn.github.io/react-virtualized/) 是热门的虚拟滚动库。 +它们提供了多种可复用的组件,用于展示列表、网格和表格数据。 +如果你想要一些针对你的应用做定制优化,你也可以创建你自己的虚拟滚动组件,就像 [Twitter 所做的](https://medium.com/@paularmstrong/twitter-lite-and-high-performance-react-progressive-web-apps-at-scale-d28a00e780a3)。 -## Avoid Reconciliation {#avoid-reconciliation} +## 避免调停 {#avoid-reconciliation} -React builds and maintains an internal representation of the rendered UI. It includes the React elements you return from your components. This representation lets React avoid creating DOM nodes and accessing existing ones beyond necessity, as that can be slower than operations on JavaScript objects. Sometimes it is referred to as a "virtual DOM", but it works the same way on React Native. +React 构建并维护了一套内部的 UI 渲染描述。它包含了来自你的组件返回的 React 元素。该描述使得 React 避免创建 DOM 节点以及没有必要的节点访问,因为 DOM 操作相对于 JavaScript 对象操作更慢。虽然有时候它被称为“虚拟 DOM”,但是它在 React Native 中拥有相同的工作原理。 -When a component's props or state change, React decides whether an actual DOM update is necessary by comparing the newly returned element with the previously rendered one. When they are not equal, React will update the DOM. +当一个组件的 props 或 state 变更,React 会将最新返回的元素与之前渲染的元素进行对比,以此决定是否有必要更新真实的 DOM。当它们不相同时,React 会更新该 DOM。 -You can now visualize these re-renders of the virtual DOM with React DevTools: +你可以通过 React 开发者工具可视化地查看这些重新渲染的虚拟 DOM: -- [Chrome Browser Extension](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en) -- [Firefox Browser Extension](https://addons.mozilla.org/en-GB/firefox/addon/react-devtools/) -- [Standalone Node Package](https://www.npmjs.com/package/react-devtools) +- [Chrome 浏览器扩展](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en) +- [Firefox 浏览器扩展](https://addons.mozilla.org/en-GB/firefox/addon/react-devtools/) +- [独立 Node 包](https://www.npmjs.com/package/react-devtools) -In the developer console select the **Highlight Updates** option in the **React** tab: +在开发者控制台的 **React** 标签勾选 **Highlight Updates**: -
How to enable highlight updates
+
如何开启更新高亮
-Interact with your page and you should see colored borders momentarily appear around any components that have re-rendered. This lets you spot re-renders that were not necessary. You can learn more about this React DevTools feature from this [blog post](https://blog.logrocket.com/make-react-fast-again-part-3-highlighting-component-updates-6119e45e6833) from [Ben Edelstein](https://blog.logrocket.com/@edelstein). +当与你的页面进行交互时,你会看到被重新渲染的组件立刻出现了彩色的边框。这能帮助你找到那些没有必要的重新渲染。你可以在 [Ben Edelstein](https://blog.logrocket.com/@edelstein) 的[这篇博客](https://blog.logrocket.com/make-react-fast-again-part-3-highlighting-component-updates-6119e45e6833)中学到更多关于 React 开发者工具的功能。 -Consider this example: +考虑这种情况: -
React DevTools Highlight Updates example
+
React 开发者工具更新高亮示例
-Note that when we're entering a second todo, the first todo also flashes on the screen on every keystroke. This means it is being re-rendered by React together with the input. This is sometimes called a "wasted" render. We know it is unnecessary because the first todo content has not changed, but React doesn't know this. +注意到当我们输入第二个待办事项时,第一个待办事项在每次按键时也一并闪烁了。这意味着输入时,它也被 React 一并重新渲染了。这通常被称作“无用的”渲染。我们知道这是毫无必要的,因为第一个待办事项并没有改变,但是 React 并不知道。 -Even though React only updates the changed DOM nodes, re-rendering still takes some time. In many cases it's not a problem, but if the slowdown is noticeable, you can speed all of this up by overriding the lifecycle function `shouldComponentUpdate`, which is triggered before the re-rendering process starts. The default implementation of this function returns `true`, leaving React to perform the update: +即使 React 只更新改变了的 DOM 节点,重新渲染仍然花费了一些时间。在大部分情况下它并不是问题,不过如果它已经慢到让人注意了,你可以通过覆盖生命周期方法 `shouldComponentUpdate` 来进行提速。该方法会在重新渲染前被触发。其默认实现总是返回 `true`,让 React 执行更新: ```javascript shouldComponentUpdate(nextProps, nextState) { @@ -238,27 +240,27 @@ shouldComponentUpdate(nextProps, nextState) { } ``` -If you know that in some situations your component doesn't need to update, you can return `false` from `shouldComponentUpdate` instead, to skip the whole rendering process, including calling `render()` on this component and below. +如果你知道在什么情况下你的组件不需要更新,你可以在 `shouldComponentUpdate` 中返回 `false` 来跳过整个渲染过程。其包括该组件的 `render` 调用以及之后的操作。 -In most cases, instead of writing `shouldComponentUpdate()` by hand, you can inherit from [`React.PureComponent`](/docs/react-api.html#reactpurecomponent). It is equivalent to implementing `shouldComponentUpdate()` with a shallow comparison of current and previous props and state. +在大部分情况下,你可以继承 [`React.PureComponent`](/docs/react-api.html#reactpurecomponent) 以代替手写 `shouldComponentUpdate()`。它用当前与之前 props 和 state 的浅比较覆写了 `shouldComponentUpdate()` 的实现。 -## shouldComponentUpdate In Action {#shouldcomponentupdate-in-action} +## shouldComponentUpdate 的作用 {#shouldcomponentupdate-in-action} -Here's a subtree of components. For each one, `SCU` indicates what `shouldComponentUpdate` returned, and `vDOMEq` indicates whether the rendered React elements were equivalent. Finally, the circle's color indicates whether the component had to be reconciled or not. +这是一个组件的子树。每个节点中,`SCU` 代表 `shouldComponentUpdate` 返回的值,而 `vDOMEq` 代表返回的 React 元素是否相同。最后,圆圈的颜色代表了该组件是否需要被调停。
-Since `shouldComponentUpdate` returned `false` for the subtree rooted at C2, React did not attempt to render C2, and thus didn't even have to invoke `shouldComponentUpdate` on C4 and C5. +节点 C2 的 `shouldComponentUpdate` 返回了 `false`,React 因而不会去渲染 C2,也因此 C4 和 C5 的 `shouldComponentUpdate` 不会被调用到。 -For C1 and C3, `shouldComponentUpdate` returned `true`, so React had to go down to the leaves and check them. For C6 `shouldComponentUpdate` returned `true`, and since the rendered elements weren't equivalent React had to update the DOM. +对于 C1 和 C3,`shouldComponentUpdate` 返回了 `true`,所以 React 需要继续向下查询子节点。这里 C6 的 `shouldComponentUpdate` 返回了 `true`,同时由于渲染的元素与之前的不同使得 React 更新了该 DOM。 -The last interesting case is C8. React had to render this component, but since the React elements it returned were equal to the previously rendered ones, it didn't have to update the DOM. +最后一个有趣的例子是 C8。React 需要渲染这个组件,但是由于其返回的 React 元素和之前渲染的相同,所以不需要更新 DOM。 -Note that React only had to do DOM mutations for C6, which was inevitable. For C8, it bailed out by comparing the rendered React elements, and for C2's subtree and C7, it didn't even have to compare the elements as we bailed out on `shouldComponentUpdate`, and `render` was not called. +显而易见,你看到 React 只改变了 C6 的 DOM。对于 C8,通过对比了渲染的 React 元素跳过了渲染。而对于 C2 的子节点和 C7,由于 `shouldComponentUpdate` 使得 `render` 并没有被调用。因此它们也不需要对比元素了。 -## Examples {#examples} +## 示例 {#examples} -If the only way your component ever changes is when the `props.color` or the `state.count` variable changes, you could have `shouldComponentUpdate` check that: +如果你的组件只有当 `props.color` 或者 `state.count` 的值改变才需要更新时,你可以使用 `shouldComponentUpdate` 来进行检查: ```javascript class CounterButton extends React.Component { @@ -289,7 +291,7 @@ class CounterButton extends React.Component { } ``` -In this code, `shouldComponentUpdate` is just checking if there is any change in `props.color` or `state.count`. If those values don't change, the component doesn't update. If your component got more complex, you could use a similar pattern of doing a "shallow comparison" between all the fields of `props` and `state` to determine if the component should update. This pattern is common enough that React provides a helper to use this logic - just inherit from `React.PureComponent`. So this code is a simpler way to achieve the same thing: +在这段代码中,`shouldComponentUpdate` 仅检查了 `props.color` 或 `state.count` 是否改变。如果这些值没有改变,那么这个组件不会更新。如果你的组件更复杂一些,你可以使用类似“浅比较”的模式来检查 `props` 和 `state` 中所有的字段,以此来决定是否组件需要更新。React 已经提供了一位好帮手来帮你实现这种常见的模式 - 你只要继承 `React.PureComponent` 就行了。所以这段代码可以改成以下这种更简洁的形式: ```js class CounterButton extends React.PureComponent { @@ -310,9 +312,7 @@ class CounterButton extends React.PureComponent { } ``` -Most of the time, you can use `React.PureComponent` instead of writing your own `shouldComponentUpdate`. It only does a shallow comparison, so you can't use it if the props or state may have been mutated in a way that a shallow comparison would miss. - -This can be a problem with more complex data structures. For example, let's say you want a `ListOfWords` component to render a comma-separated list of words, with a parent `WordAdder` component that lets you click a button to add a word to the list. This code does *not* work correctly: +大部分情况下,你可以使用 `React.PureComponent` 来代替手写 `shouldComponentUpdate`。但它只进行浅比较,所以当 props 或者 state 某种程度是可变的话,浅比较会有遗漏,那你就不能使用它了。当数据结构很复杂时,情况会变得麻烦。例如,你想要一个 `ListOfWords` 组件来渲染一组用逗号分开的单词。它有一个叫做 `WordAdder` 的父组件,该组件允许你点击一个按钮来添加一个单词到列表中。以下代码*并不*正确: ```javascript class ListOfWords extends React.PureComponent { @@ -331,7 +331,7 @@ class WordAdder extends React.Component { } handleClick() { - // This section is bad style and causes a bug + // 这部分代码很糟,而且还有 bug const words = this.state.words; words.push('marklar'); this.setState({words: words}); @@ -348,11 +348,11 @@ class WordAdder extends React.Component { } ``` -The problem is that `PureComponent` will do a simple comparison between the old and new values of `this.props.words`. Since this code mutates the `words` array in the `handleClick` method of `WordAdder`, the old and new values of `this.props.words` will compare as equal, even though the actual words in the array have changed. The `ListOfWords` will thus not update even though it has new words that should be rendered. +问题在于 `PureComponent` 仅仅会对新老 `this.props.words` 的值进行简单的对比。由于代码中 `WordAdder` 的 `handleClick` 方法改变了同一个 `words` 数组,使得新老 `this.props.words` 比较的其实还是同一个数组。即便实际上数组中的单词已经变了,但是比较结果是相同的。可以看到,即便多了新的单词需要被渲染, `ListOfWords` 却并没有被更新。 -## The Power Of Not Mutating Data {#the-power-of-not-mutating-data} +## 不可变数据的力量 {#the-power-of-not-mutating-data} -The simplest way to avoid this problem is to avoid mutating values that you are using as props or state. For example, the `handleClick` method above could be rewritten using `concat` as: +避免该问题最简单的方式是避免更改你正用于 props 或 state 的值。例如,上面 `handleClick` 方法可以用 `concat` 重写: ```javascript handleClick() { @@ -362,7 +362,7 @@ handleClick() { } ``` -ES6 supports a [spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator) for arrays which can make this easier. If you're using Create React App, this syntax is available by default. +ES6 数组支持[扩展运算符](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator),这让代码写起来更方便了。如果你在使用 Create React App,该语法已经默认支持了。 ```js handleClick() { @@ -372,7 +372,7 @@ handleClick() { }; ``` -You can also rewrite code that mutates objects to avoid mutation, in a similar way. For example, let's say we have an object named `colormap` and we want to write a function that changes `colormap.right` to be `'blue'`. We could write: +你可以用类似的方式改写代码来避免可变对象的产生。例如,我们有一个叫做 `colormap` 的对象。我们希望写一个方法来将 `colormap.right` 设置为 `'blue'`。我们可以这么写: ```js function updateColorMap(colormap) { @@ -380,7 +380,7 @@ function updateColorMap(colormap) { } ``` -To write this without mutating the original object, we can use [Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) method: +为了不改变原本的对象,我们可以使用 [Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) 方法: ```js function updateColorMap(colormap) { @@ -388,9 +388,9 @@ function updateColorMap(colormap) { } ``` -`updateColorMap` now returns a new object, rather than mutating the old one. `Object.assign` is in ES6 and requires a polyfill. +现在 `updateColorMap` 返回了一个新的对象,而不是修改老对象。`Object.assign` 是 ES6 的方法,需要 polyfill。 -There is a JavaScript proposal to add [object spread properties](https://github.com/sebmarkbage/ecmascript-rest-spread) to make it easier to update objects without mutation as well: +这里有一个 JavaScript 的提案,旨在添加[对象扩展属性](https://github.com/sebmarkbage/ecmascript-rest-spread)以使得更新不可变对象变得更方便: ```js function updateColorMap(colormap) { @@ -398,17 +398,17 @@ function updateColorMap(colormap) { } ``` -If you're using Create React App, both `Object.assign` and the object spread syntax are available by default. +如果你在使用 Create React App,`Object.assign` 以及对象扩展运算符已经默认支持了。 -## Using Immutable Data Structures {#using-immutable-data-structures} +## 使用不可变数据结构 {#using-immutable-data-structures} -[Immutable.js](https://github.com/facebook/immutable-js) is another way to solve this problem. It provides immutable, persistent collections that work via structural sharing: +[Immutable.js](https://github.com/facebook/immutable-js) 是另一种解决方案。它通过结构共享提供了不可变、持久化集合: -* *Immutable*: once created, a collection cannot be altered at another point in time. -* *Persistent*: new collections can be created from a previous collection and a mutation such as set. The original collection is still valid after the new collection is created. -* *Structural Sharing*: new collections are created using as much of the same structure as the original collection as possible, reducing copying to a minimum to improve performance. +* *不可变*:一旦创建,一个集合便不能再被修改。 +* *持久化*:对集合进行修改,会创建一个新的集合。之前的集合仍然有效。 +* *结构共享*:新的集合会尽可能复用之前集合的结构,以最小化拷贝操作来提高性能。 -Immutability makes tracking changes cheap. A change will always result in a new object so we only need to check if the reference to the object has changed. For example, in this regular JavaScript code: +不可变数据使得追踪变更非常容易。每次变更都会生成一个新的对象使得我们只需要检查对象的引用是否改变。举个例子,这是一段很常见的 JavaScript 代码: ```javascript const x = { foo: 'bar' }; @@ -417,7 +417,7 @@ y.foo = 'baz'; x === y; // true ``` -Although `y` was edited, since it's a reference to the same object as `x`, this comparison returns `true`. You can write similar code with immutable.js: +由于 `y` 被指向和 `x` 相同的对象,虽然我们修改了 `y`,但是对比结果还是 `true`。你可以使用 immutable.js 来写相似的代码: ```javascript const SomeRecord = Immutable.Record({ foo: null }); @@ -428,8 +428,8 @@ x === y; // false x === z; // true ``` -In this case, since a new reference is returned when mutating `x`, we can use a reference equality check `(x === y)` to verify that the new value stored in `y` is different than the original value stored in `x`. +在这个例子中,修改 `x` 后我们得到了一个新的引用,我们可以通过判断引用 `(x === y)` 来验证 `y` 中存的值和原本 `x` 中存的值不同。 -Two other libraries that can help use immutable data are [seamless-immutable](https://github.com/rtfeldman/seamless-immutable) and [immutability-helper](https://github.com/kolodny/immutability-helper). +还有两个其他的库可以帮助使用不可变数据,它们是 [seamless-immutable](https://github.com/rtfeldman/seamless-immutable) 和 [immutability-helper](https://github.com/kolodny/immutability-helper)。 -Immutable data structures provide you with a cheap way to track changes on objects, which is all we need to implement `shouldComponentUpdate`. This can often provide you with a nice performance boost. +不可变数据结构使你可以方便地追踪对象的变化,这是应用 `shouldComponentUpdate` 所需要的。让性能得以提升。