消除常见react性能问题

2019-03-19  本文已影响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

2.png

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

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

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,类似于事件捕获。

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 })
      }
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() }}>

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

上一篇 下一篇

猜你喜欢

热点阅读