为什么使用?

React15.3中新加了一个 PureComponent 类,顾名思义, pure 是纯的意思, PureComponent 也就是纯组件,取代其前身 PureRenderMixin , PureComponent 是优化 React 应用程序最重要的方法之一,易于实施,只要把继承类从 Component 换成 PureComponent 即可,可以减少不必要的 render操作的次数,从而提高性能,而且可以少写 shouldComponentUpdate 函数,节省了点代码。

原理

当组件更新时,如果组件的 props 和 state 都没发生改变, render 方法就不会触发,省去 Virtual DOM 的生成和比对过程,达到提升性能的目的。具体就是 React 自动帮我们做了一层浅比较:


1
2
3
4

if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps)
|| !shallowEqual(inst.state, nextState);
}

而 shallowEqual 又做了什么呢?会比较 Object.keys(state | props) 的长度是否一致,每一个 key 是否两者都有,并且是否是一个引用,也就是只比较了第一层的值,确实很浅,所以深层的嵌套数据是对比不出来的。

使用指南

易变数据不能使用一个引用

案例:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

class App extends PureComponent {
state = {
items: [1, 2, 3]
}
handleClick = () => {
const { items } = this.state;
items.pop();
this.setState({ items });
}
render() {
return (<div>
<ul>
{this.state.items.map(i => <li key={i}>{i}</li>)}
</ul>
<button onClick={this.handleClick}>delete</button>
</div>)
}
}

会发现,无论怎么点 delete 按钮, li 都不会变少,因为 items 用的是一个引用, shallowEqual 的结果为 true 。改正:


1
2
3
4
5

handleClick = () => {
const { items } = this.state;
items.pop();
this.setState({ items: [].concat(items) });
}

这样每次改变都会产生一个新的数组,也就可以 render 了。这里有一个矛盾的地方,如果没有 items.pop(); 操作,每次 items 数据并没有变,但还是 render 了,这不就很操蛋么?呵呵,数据都不变,你 setState 干嘛?

不变数据使用一个引用

子组件数据

上面易变数据不能使用一个引用的案例中有一个点击删除操作,如果我们删除的代码这么写:


1
2
3
4
5

handleClick = () => {
const { items } = this.state;
items.splice(items.length - 1, 1);
this.setState({ items });
}

items 的引用也是改变的,但如果 items 里面是引用类型数据:


1

items: [{a: 1}, {a: 2}, {a: 3}]

这个时候


1

state.items[0] === nextState.items[0] // false

子组件里还是re-render了。这样就需要我们保证不变的子组件数据的引用不能改变。这个时候可以使用immutable-js函数库。

函数属性

我们在给组件传一个函数的时候,有时候总喜欢:


1
2
3
4
5
6
7
8
9

// 1
<MyInput onChange={e => this.props.update(e.target.value)} />
// 2
update(e) {
this.props.update(e.target.value)
}
render() {
return <MyInput onChange={this.update.bind(this)} />
}

由于每次 render 操作 MyInput 组件的 onChange 属性都会返回一个新的函数,由于引用不一样,所以父组件的 render 也会导致 MyInput 组件的 render ,即使没有任何改动,所以需要尽量避免这样的写法,最好这样写:


1
2
3
4
5
6
7

// 1,2
update = (e) => {
this.props.update(e.target.value)
}
render() {
return <MyInput onChange={this.update} />
}

空对象、空数组或固定对象

有时候后台返回的数据中,数组长度为0或者对象没有属性会直接给一个 null ,这时候我们需要做一些容错:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

class App extends PureComponent {
state = {
items: [{ name: 'test1' }, null, { name: 'test3' }]
}
store = (id, value) => {
const { items } = this.state;
items[id] = assign({}, items[id], { name: value });
this.setState({ items: [].concat(items) });
}
render() {
return (<div>
<ul>
{this.state.items.map((i, k) =>
<Item style={{ color: 'red' }} store={this.store} key={k} id={k} data={i || {}} />)
}
</ul>
</div>)
}
}

当某一个子组件调用 store 函数改变了自己的那条属性,触发 render 操作,如果数据是 null 的话 data 属性每次都是一个 {}{} ==== {} 是 false 的,这样无端的让这几个子组件重新 render 了。{ color: 'red' }也是一样。

最好设置一个 defaultValue 为 {},如下:


1
2
3

static defaultValue = {}
const style = { color: 'red' };
<Item style={style} store={this.store} key={k} id={k} data={i || defaultValue} />

复杂状态与简单状态不要共用一个组件

这点可能和 PureComponent 没多少关系,但做的不好可能会浪费很多性能,比如一个页面上面一部分是一个复杂的列表,下面是一个输入框,抽象代码:


1
2
3
4
5
6
7
8
9
10
11

change = (e) => {
this.setState({ value: e.target.value });
}
render() {
return (<div>
<ul>
{this.state.items.map((i, k) => <li key={k}> {...}</li>)}
</ul>
<input value={this.state.value} onChange={this.change} />
</div>)
}

表单和列表其实是没有什么关联的,表单的值也可能经常变动,但它的会给列表也带来必然的 diff操作,这是没必要的,最好是给列表抽出成一个单独的 PureComponent 组件,这样 state.items 不变的话,列表就不会重新 render 了。

与 shouldComponentUpdate 共存

如果 PureComponent 里有 shouldComponentUpdate 函数的话,直接使用 shouldComponentUpdate的结果作为是否更新的依据,没有 shouldComponentUpdate 函数的话,才会去判断是不是 PureComponent ,是的话再去做 shallowEqual 浅比较。


1
2
3
4
5
6
7
8
9
10
11

// 这个变量用来控制组件是否需要更新
var shouldUpdate = true;
// inst 是组件实例
if (inst.shouldComponentUpdate) {
shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext);
} else {
if (this._compositeType === CompositeType.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps) ||
!shallowEqual(inst.state, nextState);
}
}

老版本兼容写法


1
2
3
4
5

import React { PureComponent, Component } from 'react';
 
class Foo extends (PureComponent || Component) {
//...
}

这样在老版本的 React 里也不会挂掉。

总结

PureComponent 真正起作用的,只是在一些纯展示组件上,复杂组件用了也没关系,反正 shallowEqual 那一关就过不了,不过记得 props 和 state 不能使用同一个引用哦。

React性能优化 PureComponent的更多相关文章

  1. React性能优化之PureComponent 和 memo使用分析

    前言 关于react性能优化,在react 16这个版本,官方推出fiber,在框架层面优化了react性能上面的问题.由于这个太过于庞大,我们今天围绕子自组件更新策略,从两个及其微小的方面来谈rea ...

  2. react性能优化

    前面的话 本文将详细介绍react性能优化 避免重复渲染 当一个组件的props或者state改变时,React通过比较新返回的元素和之前渲染的元素来决定是否有必要更新实际的DOM.当他们不相等时,R ...

  3. React性能优化记录(不定期更新)

    React性能优化记录(不定期更新) 1. 使用PureComponent代替Component 在新建组件的时候需要继承Component会用到以下代码 import React,{Componen ...

  4. 关于React性能优化

    这几天陆陆续续看了一些关于React性能优化的博客,大部分提到的都是React 15.3新加入的PureComponent ,通过使用这个类来减少React的重复渲染,从而提升页面的性能.使用过Rea ...

  5. React 性能优化 All In One

    React 性能优化 All In One Use CSS Variables instead of React Context https://epicreact.dev/css-variables ...

  6. react组件性能优化PureComponent

    首先我们使用react组件会配合connect来连接store获取state,那么只要store中的state发生改变组件就会重新渲染,所以性能不高,一般我们可以使用shouldComponentUp ...

  7. React性能优化总结(转)

    原文链接: https://segmentfault.com/a/1190000007811296?utm_source=tuicool&utm_medium=referral 初学者对Rea ...

  8. react性能优化要点

    1.减少render方法的调用 1.1继承React.PureComponent(会自动在内部使用shouldComponentUpdate方法对state或props进行浅比较.)或在继承自Reac ...

  9. React性能优化总结

    本文主要对在React应用中可以采用的一些性能优化方式做一下总结整理 前言 目的 目前在工作中,大量的项目都是使用react来进行开展的,了解掌握下react的性能优化对项目的体验和可维护性都有很大的 ...

随机推荐

  1. Strategy(策略)

    意图: 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换.本模式使得算法可独立于使用它的客户而变化. 适用性: 许多相关的类仅仅是行为有异.“策略”提供了一种用多个行为中的一个行为来配置 ...

  2. 查找一个Class到底在那一个jar文件里

    整理自己的一些笔记,发觉这个命令 ,看起来是用来找一个Class到底在那一个jar文件里的. 虽然没有再测一下,估计是好使的. 先在博客园里记下来,防止自己忘掉. findstr /S /M org. ...

  3. Python环境管理--virtualenvwrapper

    遇到问题: 当最近的开发和部署过程中,多个服务器部署的时候发现对于库和包的管理非常混乱,主要有俩个版本问题: 因为业务需要,代码得分别部署在不同的服务器上面,每次部署的时候都得重复的安装包而且不能确定 ...

  4. 谈谈oracle里的join、left join、right join

    create table l as select 'left_1' as str,'1' as v from dual union allselect 'left_2' ,'2' as v from ...

  5. Java中字符串比较的注意点

    Java中必须使用string1.equals(string2)来进行判断 补充如果: string s1=new String("Hello"); string s2=new S ...

  6. 微信小程序scroll-view横向滚动

    官方文档给的代码复制下来发现无法滚动,没反应,使用css设置浮动属性也无效 官方没有给出css代码,横向需要设置两个css属性才行: white-space: nowrap; ----规定段落中的文本 ...

  7. wikioi 1028 花店橱窗布置 最大权匹配

    中文题意不描述. 链接:http://wikioi.com/problem/1028/ 这题一开始很裸的最大权二分匹配.但是原来没有接触过,KM的这个最大权不大会.然后一开始以为用最大费用最大流直接就 ...

  8. Deploying Qt or how to avoid “could not find or load the Qt platform plugin”

    (转自:http://www.tripleboot.org/?p=138) Once you’ve built your first Qt program, have you tried it on ...

  9. poj2159

    题解: 记录一下每个串每个字母出现的次数 排序 然后看看是否相等 代码: #include<cstdio> #include<cstring> #include<cstr ...

  10. linux 命令 --if

    if else-if else 语法格式: if condition1 then command1 elif condition2 then command2 else commandN fi 例如: ...