美文网首页
消除常见react性能问题

消除常见react性能问题

作者: 糖糖不加糖_ | 来源:发表于2019-03-19 10:16 被阅读0次

1、 Performance面板

1. FPS、CPU和NET

1.png

FPS(frames per second):是用来分析动画的一个主要性能指标,理论上是越高越好,(但过高的刷新率并没有实际意义,一般人很难分辨出60帧/秒与100帧/秒有什么不同。)能达到60帧/秒的话,在用户体验度的方向上是不错的存在,在FPS图表中如果存在红色的长条,说明帧存在严重的问题,可能导致非常差的用户体验,一般来说,绿色的长条越高,说明FPS越高,用户体验越好。

CPU:鼠标点击CPU图标,下方的Summary面板上会呈现此时刻CPU使用信息,各种颜色代表着在这个时间段内,CPU在各种处理上所花费的时间。

NET:每条彩色横杠表示一种资源。横杠越长,检索资源所需的时间越长。 每个横杠的浅色部分表示等待时间(从请求资源到第一个字节下载完成的时间)

PS 显示实时FPS面板,实时展示页面的FPS指标

1、按下 Command+Shift+P(Mac)或者 Control+Shift+P(Windows, Linux) 打开命令菜单

2、输入Rendering,点选Show Rendering

3、在Rendering面板里,激活FPS Meter。FPS实时面板就出现在页面的右上方。

4、按下Esc关闭FPS Meter

  • Summary面板

2.png

summary面板为总结面板,从宏观层面上概括了浏览器加载的总时间,包括以下几个部分

颜色 英文 中文
蓝色 Loading 加载
黄色 Scripting 脚本
紫色 Rendering 渲染
绿色 Painting 绘制
灰色 Other 其他
白色 Idle 空闲
  • Bottom-Up和Call Tree

Bottom-Up和Call Tree两者都是直观地分析浏览器对页面的build精确到毫秒级的情况,前者是The Heavy (Bottom Up) view is available in the Bottom-Up tab,类似于事件冒泡;后者是And the Tree (Top Down) view is available in the Call Tree tab,类似于事件捕获。

  • Event Log

3.png

Event Log可以查看某一阶段的日志,点击各个模块的按钮,查看日志信息

2、消除常见react性能问题

  • 将经常更新的区域提取为孤立的组件

在react应用程序中最常遇见兼得问题就是渲染问题,当渲染一个大型Dom树时会消耗很多时间,大部分时候,并不需要将整个DOm重新渲染,而是只改变某个分支上的某一个值,频繁的渲染操作将会造成渲染上的浪费,一个比较好的方式是将经常更新的区域拆分成一个单独的组件。

父组件

import React from 'react';
import Title from './Title/index';
export default class SuperList extends React.Component {  // 首次渲染会渲染两次
    constructor(props) {
        super(props)
        this.state = {
          items: [1, 2, 3],
        }
    }
    addItem = () => {
        const self = this;
        let {items} = self.state;
        items.push(4);
        this.setState({ items });
    }
    render() {
        console.log('父组件 render')
        const { items, obj } = this.state;
        return (
          <div>
            <ul>
              {items.map((e, i) => {
                return <li key={i}>数组第{i}个值为:{e}</li>
              })}
            </ul>
            <button onClick={this.addItem}>新增数组值</button>  
            <Title />      
          </div>
        )
      }
}

子组件

import React from 'react';
export default class Title extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
          title: 'title',
        }
        // this.changeTitle = this.changeTitle.bind(this); // 当使用ES5编写函数changeTitle() {}时,必须使用这个
      }
      changeTitle = () => {
        const self = this;
        let {title} = self.state;
        title = '你好';
        this.setState({ title });
      }
      render() {
        console.log('子组件render')
        return (
          <div>
            <div>子组件title值:{this.state.title}</div>
            <button onClick={this.changeTitle}>更改title值</button>
          </div>
        )
      }
}

在上述组件中将DOM树拆分存储为两个组件,将频繁渲染DOM的部分单独拆分成一个组件,当富组件渲染时,会带动子组件进行渲染,但子组件渲染时,在此组件条件下,父组件中的DOM不会被重新渲染,需要渲染的DOM树减少,能够增加渲染的速度。

  • 使用纯组件方式

使用纯组件的前提是组件的props与之前的props不同,这样的话当前组件才会被重新渲染,实现纯组件的简单方法是使用React.PureComponent,而不是使用默认组件React.Component,React.PureComponent与React.Component的区别在于React.Component没有实现shouldComponentUpdate(),而React.PureComponent通过浅的props和state比较来实现它。

⚠️ React.PureComponent中的shouldComponentUpdate()只是浅解析对象,对于包含复杂的数据结构,可能会出现渲染错误

当使用React.PureComponent时,希望props中具有简单的数据变化,或者当编码人员知道有深层的数据变化时使用forceUpdate()对数据做更新渲染DOM树。

父组件

import React from 'react';
import Title from './Title/index';
export default class SuperList extends React.PureComponent {
    constructor(props) {
        super(props)
        this.state = {
          items: [1, 2, 3],
          obj: {a: 1, b: 2},
          title: '早上好',
        }
      }

    addItem = () => {
        const self = this;
        let {items, obj } = self.state;
        items.push(4);
        obj.b = 3;
        // this.forceUpdate();
        this.setState({ items, obj });
    }
    render() {
        console.log('父组件 render')
        const { title, items, obj } = this.state;
        return (
          <div>
            <ul>
              {items.map((e, i) => {
                return <li key={i}>数组第{i}个值为:{e}</li>
              })}
            </ul>
            <div>对象中的值b为:{obj.b}</div>
            <button onClick={this.addItem}>新增数组值</button>
            <Title title={title} />
          </div>
        )
      }
}

子组件

import React from 'react';
export default class Title extends React.PureComponent {
    constructor(props) {
        super(props);
        this.state = {
          title: 'title',
        }
    }
    // shouldComponentUpdate(nextProps) {
    //     if (nextProps.title !== this.props.title) {
    //         return true;
    //     }
    //     return false;
    // }
    changeTitle = () => {
        const self = this;
        let {title} = self.state;
        title = '你好';
        this.setState({ title });
    }
    render() {
        console.log('子组件render')
        return (
          <div>
            <div>子组件title值:{this.state.title}</div>
            <button onClick={this.changeTitle}>更改title值</button>
          </div>
        )
      }
}

使用React.PureComponent可以解决React.Component在首次渲染时会渲染两次的问题,因为React.PureComponent是浅比较是否有质的改变,当值不存在改变时不会渲染。上面也会出现一个问题,当父组件点击改变数组按钮时,改组件也不会被渲染,因为发生改变的是数组和对象,即使是重新的setState了,但数组和对象的地址并未发生变化,所以不会有渲染发生,随后使用的forceUpdata()方法强制刷新,会使组件重新渲染。

除了使用forceUpdate()函数之外,还可以使用以下几种方式(但都没有forceUpdate()实行简单):

  • 深拷贝
add() {
        let items =JSON.parse(JSON.stringify(this.state.items));//黑科技
        items.push(4);
        this.setState({ items })
      }
  • 数组使用concat,对象使用Object.assign()
add() {
    let { items } = this.state;
    items=items.concat(4)  //此时的items是一个新数组
    this.setState({ items })
  }

当使用了React.PureComponent后不需要使用shouldComponentUpdate()方法,当两者全部使用时会出现如下提示,并且在上述代码中会根据shouldComponentUpdate()来判断是否渲染当前的DOM树

Warning: SuperList has a method called shouldComponentUpdate(). shouldComponentUpdate should not be used when extending React.PureComponent. Please extend React.Component if shouldComponentUpdate is used.

  • 避免将新对象作为传递参数

只要props发生变化,就会重新渲染,此时存在一种情况就是,当传入一个对象时,两个对象比较,值未发生改变,但却并不指向同一地址,此时react会认为当前是有改变的,因此会重新渲染。

父组件

import React from 'react';
import Title from './Title/index';
export default class SuperList extends React.PureComponent {
    constructor(props) {
        super(props)
        this.state = {
          title: '你好',
          name: '我是XXX',
          preObj: { title: '你好' }
        }
      }

    addItem = () => {
        const self = this;
        let { name, preObj } = self.state;
        // let { name, preObj, title } = self.state;
        name = name + name;
        // title = title + title
        preObj.title = preObj.title + preObj.title;
        // this.setState({ name, title, preObj });
        this.setState({ name, preObj });

    }
    render() {
        console.log('父组件 render')
        const { title, name, preObj } = this.state;
        const obj = {
            title
        };
        return (
          <div>
            <div>对象中的值b为:{name}</div>
            <button onClick={this.addItem}>改变姓名</button>
            {/* <Title title={title}  /> */}
            {/* <Title title={obj.title}  /> */}
            {/* <Title obj={preObj}  /> */}
            <Title obj={obj}  />
          </div>
        )
      }
}

子组件

import React from 'react';
export default class Title extends React.PureComponent {
    constructor(props) {
        super(props);
        this.state = {
          title: 'title',
        }
    }
    render() {
        console.log('子组件render')
        return (
          <div>
            <div>子组件title值:{this.props.obj.title}</div>
            {/* <div>子组件title值:{this.props.title}</div> */}
          </div>
        )
      }
}

在每次渲染时调用了新的对象,因此props传递给<Title>的值就被视为新值,因此会导致组件重新渲染

  • 父组件传递函数时优化

绑定this的方式,一般有以下几种:

1、constructor中绑定(es5)

constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this); //构造函数中绑定
}
//然后可以
<p onClick={this.handleClick}>

在es6中可以直接写成以下方式不需要绑定

<p onClick={this.handleClick}>

2、使用时绑定(es5)

<p onClick={this.handleClick.bind(this)}>

3、箭头函数

<p onClick={() => { this.handleClick() }}>

三种方式哪一种的性能更好呢个?

  • 第一种,构造函数每一次渲染的时候只会执行一遍;(最nice)

  • 第二种方法,在每次render()的时候都会重新执行一遍函数;

  • 三种方法的话,每一次render()的时候,都会生成一个新的箭头函数

相关文章

网友评论

      本文标题:消除常见react性能问题

      本文链接:https://www.haomeiwen.com/subject/alwwmqtx.html