React性能优化心得

本文是《深入React技术栈》读书笔记,其中的很多都已经运用到实际项目中

更多笔记可以star我的github,上面基本都是平时的学习笔记,以及项目中的实践心得,欢迎关注

影响网页性能一个较大的因素是浏览器的重绘reflow和重排版repaint

我们通过拆分组件为子组件,进而对组件进行更细粒度的控制。这也是函数式变成的魅力之一,保持纯净的状态;可以让方法和组件更加专注,体积更小,更独立,更有复用性和测试性

PureRender的本质

官方提供了react-addons-pure-render-mixin的插件,其原理为重新实现shouldComponentUpdate的方法,当前的stateprops与之前做浅比较,如果返回fales,就不会调用render方法

但是默认的shouldComponentUpdate方法是默认的深比较。但是深比较的代价十分昂贵,就如下面的代码

shouldComponentUpdate(nextProps, nextState) {
    return isDeepEqual(this.props, nextProps) && isDeepEqual(this.state, nextState);   
}

然而PureRender只对值进行了浅比较,以下是shollowEqual的示例代码

function shallowEqual(obj, newobj) {
    if(obj === newobj) {
        return true;
    }
}
const objkeys = Object.keys(obj);
const newobjkeys = Object.keys(newobj);
if(objkeys.length !== newobjkeys.length) {
    return false;
}

// 以下是关键代码
return objkeys.every(key => {
    return newobj[key] === newobjkeys[key];
})

运用PureRender

利用createClass构建组件时,可以使用官方插件react-addons-pure-render-mixin

import PureRenderMixin from \'react-addons-pure-render-mixin\';
class App extends Component {
    constructor(props) {
        super(props);
        this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
    }
    return <div className={this.props.className}>foo</div>
}

优化PureRender

  • 直接将props设置为对象或者数组

我们知道,每次调用React组件其实都会重新创建组件。就算传入的数组或对象的值没有改变,他们引用的地址也会改变

<Account red\'}} />

这样设置的props每次渲染都会创建新对象。这样的操作,我们只需要提前将其赋值为常量,不直接使用字面量即可

const defaultStyle = {};
<Account | defaultStyle} />
  • 设置props方法并通过事件绑定在元素上面
// 事件绑定写入到构造函数中
constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
}
  • 设置子组件
const NameItem = () => <Item><span>Arthur</span></Item>;
// 上面的组件翻译过来其实是
const NameItem = () => 
    <Item 
        children={React.createElement(\'span\', {}, \'Arthur\')}
     />

显然上面Item组件不管什么情况下都会直接渲染,那么如何避免这种情况

import React, {Component} from \'react\';
import PureRenderMixin from \'react-addons-pure-render-mixin\';

class NameItem extends Component {
    constructor(props) {
        super(props);
        this.shouldComponentUpdata = PureRenderMixin.shouldComponentUpdate.bind(this);
    }
    render() {
        return (
            <Item><spanArthur</span>></Item>
        )
    }
}

Immutable

Immutable Data就是一旦被创建,就不能更改的数据,对Immutable Data对象进行修改添加或者删除操作,都会返回一个Immutable对象,实现的原理就是持久化数据结构。

Immutable使用旧数据创建新数据时,要保证旧的数据可用但是不可变,同时为了避免将所有的节点都复制一遍带来的性能损耗,Immutable使用结构共享,即如果对象树中一个节点发生变化,只修改这个节点和它受影响的父节点

  • Map 键值对的集合,对应于Object,ES6也有专门的Map对象
  • List 有序可重复对象,对应于Array
  • ArrayList 无序不可重复列表

Immutable.is

为了直接比较两个对象的值,可以使用Immutable.is来作比较

let map1 = Immutable.Map({a: 1, b:2});
let map2 = Immutable.Map({a: 1, b:2});
Immutable.is(map1, map2); // true

Immutable.is比较的是两个对象的hashCode或valueOf,由于immutable内部使用了trie数据接口来存储,只要两个对象的hashCode相等,值就是一样的,这样做的效率非常之高

react-addons-create-fragment

当有两个子组件需要渲染时,我们没办法给他设置key。这个时候可以使用官方提供的插件

import React, {Component}   from \'react\';
import {render}             from \'react-dom\';
import createFragment       from \'react-addons-create-fragment\';

const first = <li>first</li>;
const second = <li>second</li>;

const Rank = ({first, second}) => {
    const children = createFragment({
        first,
        second
    });
    return <ul>{children}</ul>;
};

render(<Rank first={first} second={second} />, document.getElementById(\'app\'))