/**
* 修改 state 中 text 属性的函数
*/
changeText = () => {
let { text } = this.state;
text = 'World';
this.setState({
text
});
}
/**
* 修改 state 中 todo 对象的函数
*/
changeTodo = () => {
let { todo } = this.state;
todo.message = "学习 Vue";
this.setState({
todo
});
}
function ComponentDummy() {}
ComponentDummy.prototype = Component.prototype;
/**
* Convenience component with default shallow equality check for sCU.
*/
function PureComponent(props, context, updater) {
this.props = props;
this.context = context;
// If a component has string refs, we will assign a different object later.
this.refs = emptyObject;
this.updater = updater || ReactNoopUpdateQueue;
}
const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());
pureComponentPrototype.constructor = PureComponent;
// Avoid an extra prototype jump for these methods.
Object.assign(pureComponentPrototype, Component.prototype);
pureComponentPrototype.isPureReactComponent = true;
import is from './objectIs';
const hasOwnProperty = Object.prototype.hasOwnProperty;
/**
* Performs equality by iterating through keys on an object and returning false
* when any key has values which are not strictly equal between the arguments.
* Returns true when the values of all keys are strictly equal.
*/
function shallowEqual(objA: mixed, objB: mixed): boolean {
if (is(objA, objB)) {
return true;
}
if (
typeof objA !== 'object' ||
objA === null ||
typeof objB !== 'object' ||
objB === null
) {
return false;
}
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
}
// Test for A's keys different from B.
for (let i = 0; i < keysA.length; i++) {
if (
!hasOwnProperty.call(objB, keysA[i]) ||
!is(objA[keysA[i]], objB[keysA[i]])
) {
return false;
}
}
return true;
}
export default shallowEqual;
本文简要介绍了 React 中 PureComponent 与 Component 的区别以及使用时需要注意的问题,并在后面附上了源码解析,希望对有疑惑的朋友提供一些帮助。
前言
先介绍一下 PureComponent,平时我们创建 React 组件一般是继承于 Component,而 PureComponent 相当于是一个更纯净的 Component,对更新前后的数据进行了一次浅比较。只有在数据真正发生改变时,才会对组件重新进行 render。因此可以大大提高组件的性能。
本文已收录在
Github
: https://github.com/beichensky/Blog 中,欢迎 Star!对比 Component 和 PureComponent
继承 Component 创建组件
App.js
里面的
state
有两个属性,text
属性是基本数据类型,todo
属性是引用类型。针对这两种数据类型分别进行对比:浏览器中界面
测试
运行项目,打开控制台,此时看到只有一个
log
:tag render
点击 5 次 ·更改文字· 按钮,可以看到控制台再次多打印了 5 次
log
,浏览器中的Hello
文字变成了World
点击 5 次 ·更改计划· 按钮,控制台一样多打印 5 次
log
,浏览器中的学习 React
计划变成了学习 Vue
分析一下,其实 5 次点击中只有一次是有效的,后来的数据其实并没有真正改变,但是由于依然使用了
setState()
,所以还是会重新render
。所以这种模式是比较消耗性能的。继承 PureComponent
其实
PureComponent
用法也是和Component
一样,只不过是将继承Component
换成了PureComponent
。App.js
浏览器中界面
测试
和上面 Component 的测试方式一样
点击 5 次 ·更改文字· 按钮,可以看到控制台只多打印了 1 次
log
,浏览器中的Hello
文字变成了World
点击 5 次 ·更改计划· 按钮,控制台只多打印了 1 次
log
,浏览器中的学习 React
计划变成了学习 Vue
使用时可能遇到的问题
下面我们将代码中
changeText
和changeTodo
方法修改一下此时我们再重新测试一下:
点击 ·更改文字· 按钮,控制台多打印一次
log
,浏览器中的Hello
文字变成了World
注意:点击 ·更改计划· 按钮,控制台没有
log
打印,浏览器中的计划也没有发生改变PureComponent 源码解析
ReactBaseClasses.js
(Github 代码位置)可以看到
PureComponent
的使用和Component
一致,只时最后为其添加了一个isPureReactComponent
属性。ComponentDummy
就是通过原型模拟继承的方式将Component
原型中的方法和属性传递给了PureComponent
。同时为了避免原型链拉长导致属性查找的性能消耗,通过Object.assign
把属性从Component
拷贝了过来。但是这里只是
PureComponent
的声明创建,没有显示如何进行比较更新的,那我们继续看下面的代码。ReactFiberClassComponent.js
(Github 代码位置)shallowEqual
是在share
包中一个工具方法,看一下其中的内部实现吧。shallowEqual.js
(Github 代码位置)这里面还调用了
is
函数,这个函数也是share
包中的一个工具方法。objectIs.js
(Github 代码位置)PureComponent
源码分析总结由上面的源码可以发现,其实
PureComponent
和Component
中的方法和属性基本一致,只不过PureComponent
多了一个isPureReactComponent
为true
的属性。在checkShouldComponentUpdate
的时候,会根据这个属性判断是否是PureComponent
,如果是的话,就会根据!shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
这个判断语句的返回值作为更新依据。所以,查看了shallowEqual
和objectIs
的文件源码,我们可以得出PureComponent
的浅比较结论:先通过
is
函数判断两个参数是否相同,相同则直接返回 ture,也就是不更新组件。objectIs.js
代码可知,基本属性类型判断值是否相同(包括NaN
),引用数据类型判断是否是一个引用若
is
函数判断为false
,则判断两个参数是否都为 对象 且 都不为null
,若任意一个 不是对象 或 任意一个为null
,直接返回false
,也就是更新组件若前两个判断都通过,则可断定两个参数皆为对象,此时判断它们
keys
的长度是否相同,若不同,则直接返回false
,即更新组件若
keys
长度不同,则对两个对象中的第一层属性进行比较,若都相同,则返回true
,有任一属性不同,则返回false
总结
阅读源码之后,可以发现之前我们修改了
changeTodo
方法的逻辑之后,为什么数据改变,组件却依然不更新的原因了。是因为修改的是同一个对象,所以PureComponent
默认引用相同,不进行组件更新,所以才会出现这个陷阱,在使用的过程中希望大家注意一下这个问题。对比
PureComponent
和Component
,可以发现,PureComponent 性能更高,一般有几次有效修改,就会进行几次有效更新为了避免出现上面所说的陷阱问题,建议将
React
和Immutable.js
配合使用,因为Immutable.js
中的数据类型都是不可变,每个变量都不会相同。但是由于Immutable
学习成本较高,可以在项目中使用immutability-helper
插件,也能实现类似的功能。关于immutability-helper
的使用,可以查看我的另一篇博客:immutability-helper 插件的基本使用虽然
PureComponent
提高了性能,但是也只是对数据进行了一次浅比较,最能优化性能的方式还是自己在shouldComponent
生命周期中实现响应逻辑关于
PureComponent
浅比较的总结可以查看上面的 PureComponent 源码分析总结写在后面
如果有写的不对或不严谨的地方,欢迎大家能提出宝贵的意见,十分感谢。
如果喜欢或者有所帮助,欢迎 Star,对作者也是一种鼓励和支持。