前言

React15.3中新加了一个 PureComponent 类,PureComponent 也就是纯组件,取代其前身 PureRenderMixin ,

PureComponent 是优化 React 应用程序最重要的方法之一,易于实施,只要把继承类从 Component 换成 PureComponent 即可,可以减少不必要的 render操作的次数,从而提高性能,而且可以少写 shouldComponentUpdate 函数,节省了点代码。但是也有坑要注意

原理

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

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

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

实例

在React Component的生命周期中,有一个shouldComponentUpdate方法。这个方法默认返回值是true。

这意味着就算没有改变组件的props或者state,也会导致组件的重绘。这就经常导致组件因为不相关数据的改变导致重绘,这极大的降低了React的渲染效率。比如下面的例子中,任何options的变化,甚至是其他数据的变化都可能导致所有cell的重绘。


{
this.props.items.map(i =>
<Cell data={i} option={this.props.options[i]} />
)
}

重写shouldComponentUpdate

为了避免这个问题,我们可以在Cell中重写shouldComponentUpdate方法,只在option发生改变时进行重绘。

shouldComponentUpdate(nextProps, nextState) {
if (this.props.option === nextProps.option) {
return false;
} else {
return true;
}
}

这样每个Cell只有在关联option发生变化时进行重绘。

因为上面的情况十分通用,React创建了PureComponent组件创建了默认的shouldComponentUpdate行为。这个默认的shouldComponentUpdate行为会一一比较props和state中所有的属性,只有当其中任意一项发生改变是,才会进行重绘。

需要注意的是,PureComponent使用浅比较判断组件是否需要重绘

因此,下面对数据的修改并不会导致重绘(假设Table也是PureComponent)

  options.push(new Option())
options.splice(0, 1)
options[i].name = "Hello"

这写例子都是在原对象上进行修改,浅比较只是比较指针的异同,会认为不需要进行重绘。

为了避免出现这些问题,推荐使用immutable.js。immutable.js会在每次对原对象进行添加,删除,修改使返回新的对象实例。任何对数据的修改都会导致数据指针的变化。

import immutable from 'immutable';
const { Map, List } = immutable; componentDidMount(){
const map1 = Map({a:1, b: 2, c: 3})
const map2 = map1.set('b',50)
console.log(map1.get("b")) // 2
console.log(map2.get("b")) // 50 const list1 = this.state.list;
const list2 = list1.push(3,4,5)
const list3 = list2.unshift(0)
const list4 = list1.concat(list2,list3)
console.log(list1.size) //2
console.log(list2.size) //5
console.log(list3.size) //6
console.log(list4.size) //13
}

使用immutable的优点

第一点,丰富的语法糖,有了ImmutableJS,代码中就不会再出现如下内容:

//为了在不污染原对象的前提下增加新的KV
var state = Object.assign({}, state, {
key: value
}); //为了在不污染原数组的前提下插入新元素
var state = [
...state.slice(0, index),
insertData,
...state.slice(index + 1)
];

有时候,为了保证reducer在处理 state 的时候不会改变传入的 state,就要写大量的上述代码。但是有了 Immutable.js,你就可以这么写:

var state = state.set('key', value);

var state = state.splice(index, 1, value);

第二点,性能的提升。由于 immutable 内部使用了 Trie 数据结构来存储,只要两个对象的 hashCode 相等,值就是一样的。这样的算法避免了深度遍历比较,性能非常好。这对我们在进行具体渲染过程中的性能优化非常有用。

更深层的使用请参考http://react-china.org/t/react-redux-immutablejs/9948

使用PureComponent的坑

1、Literal Array与Literal Object 数组字面量 对象字面量

{
this.props.items.map(i =>
<Cell data={i} options={this.props.options || []} />
)
}

若options为空,则会使用[]。[]每次会生成新的Array,因此导致Cell每次的props都不一样,导致需要重绘。解决方法如下:

const default = [];
{
this.props.items.map(i =>
<Cell data={i} options={this.props.options || default} />
)
}

2、内联函数

函数也经常作为props传递,由于每次需要为内联函数创建一个新的实例,所以每次function都会指向不同的内存地址。比如:

render() {
<MyInput onChange={e => this.props.update(e.target.value)} />;
}

以及:

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

注意第二个例子也会导致创建新的函数实例。为了解决这个问题,需要提前绑定this指针:

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

或者使用箭头函数来解决

constructor(props) {
super(props);
}
update=(e)=> {
this.props.update(e.target.value);
}
render() {
return <MyInput onChange={this.update} />;
}

与 shouldComponentUpdate 共存

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

// 这个变量用来控制组件是否需要更新
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);
}
}

老版本兼容写法

import React { PureComponent, Component } from 'react';

class Foo extends (PureComponent || Component) {
//...
}

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

PureComponent 真正起作用的,只是在一些纯展示组件上,复杂组件我认为用了反而会更复杂,该更新的时候不更新,容易造成更多bug,源码里shallowEqual 那一关就过不了。

参考:http://www.wulv.site/2017-05-31/react-purecomponent.html

https://www.jianshu.com/p/33cda0dc316a

PureComponent的更多相关文章

  1. react中PureComponent浅对比策略

    PureComponent实现了Component中没有实现的shouComponentUpdata()方法,会对state和props进行一次浅对比,本文介绍一下浅对比策略 源码中,实现浅对比的函数 ...

  2. 从 0 到 1 实现 React 系列 —— 5.PureComponent 实现 && HOC 探幽

    本系列文章在实现一个 cpreact 的同时帮助大家理顺 React 框架的核心内容(JSX/虚拟DOM/组件/生命周期/diff算法/setState/PureComponent/HOC/...) ...

  3. React-性能优化pureComponent

    每当store里有数据更新时,render()函数就会执行,有时候store的更新数据与本组件并没有关系,render()不必执行. 我们可以用shouldComponentUpdate来优化组件. ...

  4. 【react】---pureComponent的理解

    一.pureComponent的理解  pureComponent表示一个纯组件,可以用来优化react程序.减少render函数渲染的次数.提高性能 pureComponent进行的是浅比较,也就是 ...

  5. PureComponent的作用及一些使用陷阱

    默认渲染行为的问题 在React Component的生命周期中,有一个shouldComponentUpdate方法.这个方法默认返回值是true. 这意味着就算没有改变组件的props或者stat ...

  6. React 中的 Component、PureComponent、无状态组件 之间的比较

    React 中的 Component.PureComponent.无状态组件之间的比较 table th:first-of-type { width: 150px; } 组件类型 说明 React.c ...

  7. react优化--pureComponent

    shouldComponentUpdate的默认渲染 在React Component的生命周期中,shouldComponentUpdate方法,默认返回true,也就意味着就算没有改变props或 ...

  8. React Native(六)——PureComponent VS Component

    先看两段代码: export class ywg extends PureComponent { …… render() { return ( …… ); } } export class ywg e ...

  9. anu - pureComponent

    import { inherit } from "./util"; import { Component } from "./Component"; impor ...

  10. React性能优化 PureComponent

    为什么使用? React15.3中新加了一个 PureComponent 类,顾名思义, pure 是纯的意思, PureComponent 也就是纯组件,取代其前身 PureRenderMixin  ...

随机推荐

  1. python生成excel格式座位表

    脚本分两个文件: 1.生成二维随机列表:GenerateLocaltion.py 2.将列表导入excel文件:CreateExcel.py 先上GenerateLocaltion.py: impor ...

  2. Json解析方式

    http://blog.sina.com.cn/s/blog_628cc2b70101dydc.html

  3. perl: warning: Falling back to the standard locale ("C").

    /********************************************************************************** * perl: warning: ...

  4. 8th

    2017-2018-2 20179212<网络攻防实践>第8周作业 视频学习 Kali权限维持之后门 权限维持包含Tunnel工具集.Web后门.系统后门三个子类.其中系统后门与web后门 ...

  5. NOIP 2011 DAY 2

    第一题:计算系数 题目 给定一个多项式 (ax+by)k​​,请求出多项式展开后x​​ny​m​​ 项的系数. 输入 共一行,包含 5 个整数,分别为 a,b,k,n,m,每两个整数之间用一个空格隔开 ...

  6. tiny4412sdk-1506原生uboot卡死

    于16年2月多购买了tiny4412sdk-1506,用友善之臂(以下简称友善)的superboot是可以进入linux,而用三星原始的uboot_tiny4412-20130729则不可以.出现现象 ...

  7. oracle 12c 新特性之不可见字段

    在Oracle 11g R1中,Oracle以不可见索引和虚拟字段的形式引入了一些不错的增强特性.继承前者并发扬光大,Oracle 12c 中引入了不可见字段思想.在之前的版本中,为了隐藏重要的数据字 ...

  8. 【转】Pro Android学习笔记(五三):调试和分析(1):Debug视图和DDMS视图

    目录(?)[-] Debug视图 DDMS视图 查看应用运行状态 进入debug状态 HPROF Thread信息 Method信息 Stop 截图 UI层次架构信息 其它的 Tab中提供的功能 我们 ...

  9. TS学习之Symbol

    symbol成为了一种新的原生类型,就像number和string一样(意思是可以像string一样,作为对象的属性等) Symbols是不可改变且唯一的 //symbol通过Symbol函数构造,但 ...

  10. QTP使用outlook发送邮件

    '发邮件 Dim objOutlook  Dim objOutlookMsg Dim olMailItem  ' Create the Outlook object and the new mail ...