You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
/** * 修改 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
});}
functionComponentDummy(){}ComponentDummy.prototype=Component.prototype;/** * Convenience component with default shallow equality check for sCU. */functionPureComponent(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;}constpureComponentPrototype=(PureComponent.prototype=newComponentDummy());pureComponentPrototype.constructor=PureComponent;// Avoid an extra prototype jump for these methods.Object.assign(pureComponentPrototype,Component.prototype);pureComponentPrototype.isPureReactComponent=true;
importisfrom'./objectIs';consthasOwnProperty=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. */functionshallowEqual(objA: mixed,objB: mixed): boolean{if(is(objA,objB)){returntrue;}if(typeofobjA!=='object'||objA===null||typeofobjB!=='object'||objB===null){returnfalse;}constkeysA=Object.keys(objA);constkeysB=Object.keys(objB);if(keysA.length!==keysB.length){returnfalse;}// Test for A's keys different from B.for(leti=0;i<keysA.length;i++){if(!hasOwnProperty.call(objB,keysA[i])||!is(objA[keysA[i]],objB[keysA[i]])){returnfalse;}}returntrue;}exportdefaultshallowEqual;
本文简要介绍了 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,对作者也是一种鼓励和支持。
The text was updated successfully, but these errors were encountered: