资讯专栏INFORMATION COLUMN

React性能探索 --- 避免不必要渲染

missonce / 3248人阅读

摘要:如何修复既然是不需要渲染,那就要阻止它的渲染。我个人觉得,在实际中,用跟两个工具已经可以很好帮我们判断哪部分不需要重新渲染,帮助我们做出优化。

背景

上一篇文章的结尾
http://imweb.io/topic/5985cc4...
我们说到,也许,不是所有的节点都需要重新渲染,对于那些不需要渲染的节点,我们如何找到它们并做优化呢?

本篇文章来具体解答这个问题。

应用分析

首先,先看这个应用:页面的两部分分别渲染5000个节点,从1-5000。当点击按钮之后,第二部分的节点会更新,重新渲染从2-5001的数字,但是第一部分保持不变。

</>复制代码

  1. import React, { createElement, Component } from "react";
  2. import {render} from "react-dom";
  3. import Perf from "react-addons-perf";
  4. import ListItem from "./ListItem"
  5. function arrayGenerator(length) {
  6. return Array.apply(null, { length: length }).map(Number.call, Number)
  7. }
  8. class App extends Component {
  9. constructor(props) {
  10. super(props)
  11. this.state = {
  12. multiplier: 1
  13. }
  14. }
  15. resetMultiplier() {
  16. this.setState({ multiplier: 2 })
  17. }
  18. render() {
  19. return (
    • {
    • arrayGenerator(5000).map(i => {
    • return
    • })
    • }
    • {
    • arrayGenerator(5000).map(i => {
    • return
    • })
    • }
  20. );
  21. }
  22. }
  23. render(,document.getElementById("main"));

gitbug 链接:
https://github.com/hhhuangqio...

感兴趣的同学可以下载跑一跑代码

分析更新时间

这里用react的Perf工具来测量重新渲染的时间。

使用方法:

</>复制代码

  1. npm install --save-dev react-addons-perf
  2. import Perf from "react-addons-perf"

这里主要用到四个方法:

Perf.start():开始计时

Perf.stop():结束计时

Perf.printInclusive():打印组件总的渲染时间

Perf.printWasted():打印浪费的时间

当我们点击按钮,可以看到控制台打印出下面的信息:

由控制台的数据可以看出,App用了90.59ms渲染,其中渲染ListItem的时间为55ms,渲染了10000次,其中有5000次是浪费的,因为这部分页面的内容完全没有更新的改动。

如何修复

既然是不需要渲染,那就要阻止它的渲染。React给我们提供了一个方法shouldComponentUpdate(),当这个方法返回true的时候,需要重新渲染,false的时候不需要(默认是true).

在这个栗子中,只要text的值不变,就不需要重新渲染。所以,可以这样改写ListItem 的shouldComponentUpdate

</>复制代码

  1. import React, { Component } from "react"
  2. export default class ListItem extends Component {
  3. shouldComponentUpdate(nextProps, nextState) {
  4. return nextProps.text !== this.props.text
  5. }
  6. render() {
  7. let { text } = this.props
  8. return
  9. {text}
  10. }
  11. }

在重新点击一下按钮,在控制台可以发现

App总的渲染时间降到了62.14ms,并且ListItem只重新渲染了5000个节点,完全消除了浪费的渲染。

对于上面的写法,React提供了一个新的组件PureComponent来做这件事,它会自动浅对比props/state,当两者相同的时候不渲染节点。所以,listItem又可以改写成

</>复制代码

  1. import React, { PureComponent } from "react"
  2. export default class ListItem extends PureComponent {
  3. render() {
  4. let { text } = this.props
  5. return
  6. {text}
  7. }
  8. }

跑一跑代码

通过控制台可以看到达到的效果是一样的(有点误差是正常的)。

这里再安利一个可以发现应用里是否存在不该重新渲染的节点工具:why-did-you-update

使用方法

</>复制代码

  1. 1、npm i --save-dev why-did-you-update
  2. 2、
  3. import React from "react"
  4. if (process.env.NODE_ENV !== "production") {
  5. const {whyDidYouUpdate} = require("why-did-you-update")
  6. whyDidYouUpdate(React)
  7. }

然后点击按钮看控制台


可以看到Value did not change. Avoidable re-render!的警告,是不是很实用!

注意的点

PureComponent只会浅比较,所以不适合用于深层嵌套的对象。同时,PureComponent不仅仅会跳过自己的重新渲染,还会跳过它所有子节点的,所以要注意,用它的时候是最好没有子节点并且不依赖于global state的展示型组件。

与Staleless的关系

不知道有没有人跟我有这样的疑问,无状态组件跟纯净组件有什么不同?这里做一个区分:

无状态组件只是作为一个展示组件,它的好处是:

易复用,易测试

与逻辑处理数据解耦,一般来说,app里有越多无状态组件越好,这说明逻辑处理都在上层,例如redux 中处理,这样可以在不渲染的前提下,测数据逻辑。

坏处:

没有生命周期,没办法用shouldComponentUpdate阻止重新渲染,这也就是说,它没有帮助我们提高性能的作用,这也是它跟PureComponent最大的不同。

关于如何在实际中使用这两个组件,还要根据具体的实际情况来选择~

总结

综上可以看出,减少不必要的重新渲染对于提升我们的性能有很大的意义。我个人觉得,在实际中,用Perf跟why-did-you-update两个工具已经可以很好帮我们判断哪部分不需要重新渲染,帮助我们做出优化。

遗留点

PureComponent那么好用,但是使用PureComponent是有条件的呀~

由于PureComponent只是做了一个浅比较,所以深层嵌套的对象跟数组都是比不出来的,可能会导致需要渲染的地方没有重新渲染的错误展示。

那么浅比较又是什么呢?下篇文章我们来继续探索

参考链接:

1、https://60devs.com/pure-compo...

2、https://engineering.musefind....

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/84777.html

相关文章

  • React学习之漫谈React

    摘要:事件系统合成事件的绑定方式合成事件的实现机制事件委派和自动绑定。高阶组件如果已经理解高阶函数,那么理解高阶组件也很容易的。例如我们常见的方法等都是高阶函数。对测试群众来说,从质量保证的角度出发,单元测试覆盖率是 事件系统 合成事件的绑定方式 `Test` 合成事件的实现机制:事件委派和自动绑定。 React合成事件系统的委托机制,在合成事件内部仅仅是对最外层的容器进行了绑定,并且依赖...

    darkbug 评论0 收藏0
  • 【译】JavaScript 框架的探索与变迁(下)

    摘要:对此没有任何限制,它不关心这个。一种控制变化的办法是不可改变的,持久化的数据结构。总结检测变化时开发中的核心问题,而框架们以各种方式解决这个问题。因为组件内的变化是不被允许的。 AngularJS:脏检查 我不知道什么更新了,所以当更新的时候,我只能检查所有的东西。 AngularJS 类似于 Ember,当状态改变的时候,必须人工去处理。但不同的是,AngularJS 从不同的角度来...

    CollinPeng 评论0 收藏0
  • RN 技术探索:Hermes Engine 初探

    摘要:原方式中是经过压缩的脚本文件,预编译后则是二进制文件。两者影响叠加导致整体减小,包大小得到优化。引擎包引擎包官方文档中对内存区的描述您的应用用于处理代码和资源如字节码已优化或已编译的码库和字体的内存。本文首发自普惠出行产品技术 自从 Google 的 Flutter 发布之后,Facebook 对 React-Native 的迭代开始快了起来,优化 React-Native 的性能表现...

    Cc_2011 评论0 收藏0
  • 漫谈前端性能 突破 React 应用瓶颈

    摘要:表示调用栈在下一将要执行的任务。两方性能解药我们一般有两种方案突破上文提到的瓶颈将耗时高成本高易阻塞的长任务切片,分成子任务,并异步执行这样一来,这些子任务会在不同的周期执行,进而主线程就可以在子任务间隙当中执行更新操作。 showImg(https://segmentfault.com/img/remote/1460000016008111); 性能一直以来是前端开发中非常重要的话题...

    whlong 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<