React 中 的 9 种优化技术

更新日期: 2019-09-07阅读: 2.6k标签: 优化

谷歌的数据表明,一个有 10 条数据 0.4 秒可以加载完的页面,在变成 30 条数据加载时间为 0.9 秒后,流量和广告收入减少了 20%。当谷歌地图的首页文件大小从 100kb 减少到 70~80kb 时,流量在第一周涨了 10%,接下来的三周涨了 25%。

腾讯的前端工程师根据长期的数据监控也发现页面的一秒钟延迟会造成 9.4% 的 PV 的下降,8.3% 跳出率的增加以及 3.5% 转化率的下降。

可以看出,性能优化商业上来说很重要。

但是,更重要的还是屏幕前我们的用户,让用户在使用产品时有更快更舒适的浏览体验,这算是一种前端工程师的自我修养。

所以今天就分享一下如何去优化我们的 react 项目,进而提升用户体验。


1使用React.Fragment 来避免向 dom 添加额外的节点

我们在写 React 代码时,会经常遇到返回一组元素的情况,代码像这样:

    class Parent extends React.Component {
        render() {
            return (
                <h1>Hello there!</h1>
                <h1>Hello there again!</h1>
            )
        }
    }
    如果我们写成这样,控制台会报错误:JSX parent expressions must have> ,告诉我们只能返回一个元素,所以我们通常会在最外层包裹一个 div 元素,如下所示:
      class Parent extends React.Component {
          render() {
              return (
                <div>
                  <h1>Hello there!</h1>
                  <h1>Hello there again!</h1>
                </div>
              )
          }
      }
      这样做虽然能正常执行,但是会额外创建不必要的 DOM 节点,这可能会导致创建许多无用的元素,并且在我们的渲染数据来自特定顺序的子组件时,某些情况下也会生成许多无效的节点。请考虑以下代码:
      class Table extends React.Component {
        render() {
          return (
            <table>
              <tr>
                <Columns />
              </tr>
            </table>
          );
        }
      }
      
      class Columns extends React.Component {
        render() {
          return (
            <div>
              <td>column one</td>
              <td>column two</td>
            </div>
          );
        }
      }
      上面的代码将在我们的组件中呈现以下内容:
        <table>
          <tr>
            <div>
              <td>column one</td>
              <td>column two</td>
            </div>
          </tr>
        </table>
        这显然不是我们想看到的,React 为我们提供了 Fragments,Fragments 允许我们将子列表分组,而无需向 DOM 添加额外节点。我们可以将组件重新编写为:
          class Columns extends React.Component {
            render() {
              return (
                <React.Fragment>
                  <td>column one</td>
                  <td>column two</td>
                </React.Fragment>
              );
            }
          }



          2使用 React.Lazy 延迟加载组件

          有时我们只想在请求时加载部分组件,例如,仅在单击购物车图标时加载购物车数据,在用户滚动到该点时在长图像列表的底部加载图像等。

          React.Lazy 帮助我们按需加载组件,从而减少我们应用程序的加载时间,因为只加载我们所需的组件。

          React.lazy 接受一个函数,这个函数需要动态调用 import()。它必须返回一个 Promise,该 Promise 需要 resolve 一个 defalut export 的 React 组件。如下所示:

          class MyComponent extends Component{
              render() {
                  return (<div>MyComponent</div>)
              }
          }
          const MyComponent = React.lazy(()=>import('./MyComponent.js'))
          function App() {
              return (<div><MyComponent /></div>)
          }
          在编译时,使用 webpack 解析到该语法时,它会自动地开始进行代码分割。最终,我们的应用程序将会被分成含有多个 UI 片段的包,这些 UI 片段将在需要时加载,如果你使用 Create React App,该功能已配置好,你能立刻使用这个特性。Next.js 也已支持该特性而无需再配置。


          3使用React.Suspense

          在交换组件时,会出现一个小的时间延迟,例如在 MyComponent 组件渲染完成后,包含 OtherComponent 的模块还没有被加载完成,这可能就会出现白屏的情况,我们可以使用加载指示器为此组件做优雅降级,这里我们使用 Suspense 组件来解决。

          React.Suspense 用于包装延迟组件以在加载组件时显示后备内容。

          // MyComponent.js
          const Mycomponent = React.lazy(()=>import('./component.js'))
          function App() {
              return (
              <div>
                  <Suspense fallback={<div>loading ..</div>}>
                      <MyComponent />
                  </Suspense>
              </div>)
          }

          上面的代码中,fallback 属性接受任何在组件加载过程中你想展示的 React 元素。

          你可以将 Suspense 组件置于懒加载组件之上的任何位置,你甚至可以用一个 Suspense 组件包裹多个懒加载组件。

          const OtherComponent = React.lazy(() => import('./OtherComponent'));
          const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
          
          function MyComponent() {
            return (
              <div>
                <Suspense fallback={<div>Loading...</div>}>
                  <section>
                    <OtherComponent />
                    <AnotherComponent />
                  </section>
                </Suspense>
              </div>
            );
          }


          5使用 shouldComponentUpdate() 防止不必要的重新渲染

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

          即使 React 只更新改变了的 DOM 节点,重新渲染仍然花费了一些时间。在大部分情况下它并不是问题,但是如果渲染的组件非常多时,就会浮现性能上的问题,我们可以通过覆盖生命周期方法 shouldComponentUpdate 来进行提速。

          shouldComponentUpdate 方法会在重新渲染前被触发。其默认实现总是返回 true,如果组件不需要更新,可以在 shouldComponentUpdate 中返回 false 来跳过整个渲染过程。其包括该组件的 render 调用以及之后的操作。

          shouldComponentUpdate(nextProps, nextState) {
             return nextProps.next !== this.props.next  
          }


          6使用React.PureComponent 

          React.PureComponent 与 React.Component 很相似。两者的区别在于 React.Component并未实现 shouldComponentUpdate(),而 React.PureComponent 中以浅层对比 prop 和 state 的方式来实现了该函数。

          如果赋予 React 组件相同的 props 和 state,render() 函数会渲染相同的内容,那么在某些情况下使用 React.PureComponent 可提高性能。

          // 使用 React.PureComponent
          class MyComponent extends React.PureComponent {
              render() {
                  return (<div>MyComponent</div>)
              }
          }
          
          class MyComponent extends React.Component {
              render() {
                  return (<div>MyComponent</div>)
              }
          }

          React.PureComponent 中的 shouldComponentUpdate() 仅作对象的浅层比较。如果对象中包含复杂的数据结构,则有可能因为无法检查深层的差别,产生错误的比对结果。仅在你的 props 和 state 较为简单时,才使用 React.PureComponent,或者在深层数据结构发生变化时调用 forceUpdate() 来确保组件被正确地更新。你也可以考虑使用 immutable 对象加速嵌套数据的比较。


          7使用 React.memo 来缓存组件

          React.memo 使用了缓存,缓存技术用于通过存储昂贵的函数调用的结果来加速程序,并在再次发生相同的输入时返回缓存的结果。

          如果你的函数组件在给定相同 props 的情况下渲染相同的结果,那么你可以通过将其包装在 React.memo 中调用,以此通过记忆组件渲染结果的方式来提高组件的性能表现。这意味着在这种情况下,React 将跳过渲染组件的操作并直接复用最近一次渲染的结果。
          默认情况下其只会对复杂对象做浅层对比,如果你想要控制对比过程,那么请将自定义的比较函数通过第二个参数传入来实现。
            const MyComponent = ({user}) =>{
                const {name, occupation} = user;
                return (
                    <div>
                        <h4>{name}</h4>
                        <p>{occupation}</p>
                    </div>
                )
            }
            // 比较函数
            function areEqual(prevProps, nextProps) {
              /*
              如果把 nextProps 传入 render 方法的返回结果与
              将 prevProps 传入 render 方法的返回结果一致则返回 true,
              否则返回 false
              */
            }
            export default React.memo(MyComponent, areEqual);


            8使用 ComponentDidUnmount() 删除未使用的DOM 元素 

            有些时候,存在一些未使用的代码会导致内存泄漏的问题,React 通过向我们提供componentWillUnmount 方法来解决这个问题。

            componentWillUnmount() 会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 定时器,取消网络请求或清除在 componentDidMount() 中创建的订阅等。

            例如,我们可以在组件销毁之前,清除一些事件处理程序:

              componentWillUnmount() {
                 document.removeEventListener("click", this.closeMenu);
              }

              componentWillUnmount() 中不应调用 setState(),因为该组件将永远不会重新渲染。组件实例卸载后,将永远不会再挂载它。


              9其他优化技术

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

              react-window 和 react-virtualized 是热门的虚拟滚动库。它们提供了多种可复用的组件,用于展示列表、网格和表格数据。如果你想要一些针对你的应用做定制优化,你也可以创建你自己的虚拟滚动组件,就像 Twitter 所做的。


              使用 Chrome Performance 标签分析组件

              在开发模式下,你可以通过支持的浏览器可视化地了解组件是如何 挂载、更新以及卸载的。例如:


              在 Chrome 中进行如下操作:

              1. 临时禁用所有的 Chrome 扩展,尤其是 React 开发者工具。他们会严重干扰度量结果!
              2. 确保你是在 React 的开发模式下运行应用。
              3. 打开 Chrome 开发者工具的 Performance 标签并按下 Record。
              4. 对你想分析的行为进行复现。尽量在 20 秒内完成以避免 Chrome 卡住。
              5. 停止记录。
              6. 在 User Timing 标签下会显示 React 归类好的事件。


              最后,我们探索了一些可以优化 React 应用程序的一些提高性能的方法,不局限于此。我们应该根据需要有针对性的优化应用程序,因为在某些简单的场景中,过度的优化,可能会得不偿失。

              来自:https://mp.weixin.qq.com/s/R2oGuX-WT8Muwiur8vo0qw

              链接: https://www.fly63.com/article/detial/5253

              js中for循环优化总结_如何提高程序的执行效率

              在程序开发中,经常会使用到for循环的,但是很多人写的for循环效率都是比较低的,下面就举例说明,并总结优化for循环的方法,来提高我们程序的执行效率。

              网站打开速度优化_如何提高网页访问速度技巧方法总结

              网站的加载速度不仅影响着用户体验,也会影响搜索引擎的排名,在百度推出“闪电算法”以来,将网站首屏打开速度被列入优化排名行列,作为前端开发的我们需要如果来优化网站的打开速度呢?下面就整理挖掘出很多细节上可以提升性能的东西分享给大家

              JS性能优化之文档片段createDocumentFragment

              DocumentFragments是DOM节点。它们不是主DOM树的一部分。通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM树。在DOM树中,文档片段被其所有的子元素所代替。因为文档片段存在于内存中,并不在DOM树中

              深入浅出代码优化﹣if/else

              对于代码裡面的 if else,我们可以使用逻辑判断式,或更好的三元判断式来优化代码。除了可以降低维护项目的成本之外,还可以提升代码可读性。就让我们从最简单的 if else 例子开始吧。

              微信小程序性能优化入门指南

              小程序从发布到现在也已经有将近两年的时间,越来越来多的公司开始重视小程序生态带来的流量,今年也由于小程序平台对外能力的越来越多的开放以及小程序平台的自身优化,越来越多的开发者也自主的投入到小程序的开发当中

              网络串流播放_HTML5如何优化视频文件以便在网络上更快地串流播放

              无论你正在将 GIF 动图转换为 MP4 视频,还是手头已经有一大堆 MP4 视频,你都可以优化文件结构,以使得这些视频更快地加载和播放。通过重组 atoms 将 moov 放到文件开头,浏览器可以避免发送额外的 HTTP range request 请求来搜寻和定位 moovatom

              ​web项目优化_Web 服务器性能与站点访问性能优化

              要优化 Web 服务器的性能,我们先来看看 Web 服务器在 web 页面处理上的步骤:Web 浏览器向一个特定的服务器发出 Web 页面请求; Web 服务器接收到 web 页面请求后,寻找所请求的 web 页面,并将所请求的 Web 页面传送给 Web 浏览器; 显示出来

              前端性能优化之重排和重绘

              浏览器下载完页面所有的资源后,就要开始构建DOM树,于此同时还会构建渲染树(Render Tree)。(其实在构建渲染树之前,和DOM树同期会构建Style Tree。DOM树与Style Tree合并为渲染树)

              微信小程序代码优化总汇

              写篇文章的目的,是以开放小程序代码的层面的优化。包括:条件判断将wx:if换成了hidden 、页面跳转请销毁之前使用的资源、列表的局部更新、小程序中多张图片懒加载方案、Input状态下隐藏input,应预留出键盘收起的时间

              我是如何将页面加载时间从6S降到2S的?

              生活在信息爆炸的今天,我们每天不得不面对和过滤海量的信息--无疑是焦躁和浮动的,这就意味着用户对你站点投入的时间可能是及其吝啬的(当然有一些刚需站点除外)。如何给用户提供迅速的响应就显得十分重要了

              点击更多...

              内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!