Skip to main content

Component

Component vs PureComponent

tip

PureComponent其实就是一个继承自Component的子类,会自动加载shouldComponentUpdate函数。当组件需要更新的时候,shouldComponentUpdate会对组件的props和state进行一次浅比较。如果props和state都没有发生变化,那么render方法也就不会出发,当然也就省去了之后的虚拟dom的生成和对比,在react性能方面得到了优化。

PureComponent的源码:
export defualut function PureComponent (props, context) {
Component.call(this, props, context)
}
PureComponent.prototype = Object.create(Component.prototye)
PureComponent.prototype.contructor = PureComponent
PureComponent.prototype.shouldComponentUpdate = shallowCompare

function shallowCompare (nextProps, nextState) {
return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState)
}
我们可以看到PrueComponent总体来说就是继承了Component,只不过是将shouldComponentUpdate重写成了shallowCompare。而在shallowCompare中只是返回了shallowEqual的返回值。
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;
}
tip
  • 所以从shallowEqual中可以看出,其实就是比较了传入的对象是不是一致,也就是浅比较了,props和state是不是一样。从而来实现了一个另类的shouldComponentUpdate函数。所以从源码来,PureCompoennt仅仅是一个props和state的浅比较。当props和state是对象的时候,并不能阻止不必要的渲染。所以,使用PureComponent的时候需要注意:
    • 确保数据类型是值类型;
    • 如果是引用类型,不应该有数据深层次的数据变化。
  • 区别
    • PureComponent和Component的一个最大的区别在于PureComponent会自动执行shouldComponentUpdate函数,通过shallowEqual的浅对比,实现react的性能优化。而Component必须要通过自己去调用生命周期函数shouldComponentUpdate来实现react组件的优化。
    • PureComponent不仅会影响本身,而且会影响子组件,所以PureComponent最佳情况是展示组件。
    • 若是数组和对象等引用类型,则要引用不同,才会渲染
    • 如果prop和state每次都会变,那么PureComponent的效率还不如Component,因为你知道的,进行浅比较也是需要时间
code example
import React, { useEffect } from "react"

/**
* ClassComponent
*/
class ClassComponent extends React.Component {
render() {
// 只要父级修改了props中的值,这个就会被重新渲染
console.log("1.class component was rendered.")
let { title } = this.props
return <div>class component is:{title}</div>
}
componentDidUpdate() {
// 每次props中的值修改后,这个就会被执行
console.log("============class component did update")
}
}
/**
* PureComponent
*/
class PureComponent extends React.PureComponent {
render() {
// 只有第一次加载
// 和props中的值有变化,或者父组件的state变化,这个才会执行
// 这里比较的title的shallow compare
console.log("2.pure component was rendered.")
let { title } = this.props
return <div>pure component is:{title}</div>
}
componentDidUpdates() {
// 传入的props值变化后,或者父组件的state变化,这个就会每次被执行
console.log("============pure component did update")
}
}
/**
* Parent Component
*/
export default class Parent extends React.Component {
state = {
title: "default title"
}
changeTitle = () => {
this.setState({
title: "a new Title"
})
}
changeValue = () => {
this.setState({
value: 100
})
}
render() {
return (
<>
<ClassComponent title={this.state.title} />

<PureComponent title={this.state.title} />

{/* 改变属性,会触发子组件render,
不会触发pureComponent的render */}
<button onClick={this.changeTitle}>
change props.title
</button>
{/* 改变state值,会触发子组件render,
不会触发pureComponent的render */}
<button onClick={this.changeValue}>
change state.value
</button>
</>
)
}
}

Javascript中的class

class Parent {
// new的时候,执行的构造函数可写可不写: 需要接受传递进来的实参信息,
// 才需要设置constructor
constructor(x, y) {
// this->创建的实例
this.total = x + y
}
num = 200 //等价于 this.num=2000 给实例再这是私有属性
getNum = () => {
//箭头函数没有自己的this,所用到的this是宿主环境中的
console.log(this) //this->当前创建的实例
}
sum() {
//类似于 sum=function sum() 不是箭头函数
// 它是给Parent.prototype上设置公共的方法 sum函数是不可枚举的
}
// 把构造函数当做一个普通对象,为其设置静态的私有属性方法Parent.xxx
static avg = 1000
static average() {}
}
Parent.prototype.y = 2000 //在外部手动给构造函数原生上设置公共的属性
let p = new Parent(10, 20)
console.log(p)
console.dir(Parent)

基于extends实现继承

tip
  • 首先基于call继承 React.Component.call(this) //this->Parent类的实例p
  function Component(props, context, updater) {
this.props = props
this.context = context
this.refs = emptyObject
this.updater = updater || ReactNoopUpdateQueue
}
  • 给创建的实例p设置四个私有属性: props/context/refs/updater

  • 再基于原型继承 Parent.prototype.proto === React.Component.prototype

    • 实例 -> Parent.prototype -> React.Component.prototype -> Object.prototype
    • 实例除了具备Parent.prototype提供的方法之外,还具备了React.component.prototype原型上提供的方法: isReactComponent、setState、 forceUpdate
  • 只要自己设置了constructor,则内部第一句话一定要执行 super()

class Parent extends React.Component {
constructor(props) {
// this->p props->获取的属性
/* super(); //等价于 React.Component.call(this)
// this.props=undefined this.context=undefined this.refs=l} .... */
super(props)
// this.props=props this.context=undefined ...
}
x = 100
getX() {}
}
let p = new Parent()
console.log(p)