共计 7003 个字符,预计需要花费 18 分钟才能阅读完成。
一、React.Component()
GitHub:
https://github.com/AttackXiaoJinJin/reactExplain/blob/master/react16.8.6/packages/react/src/ReactBaseClasses.js
用法:
class A extends React.Component {constructor(props){super(props) | |
this.state={}} | |
componentWillMount(){} | |
render() {return {} | |
} | |
} |
源码:
/** | |
* Copyright (c) Facebook, Inc. and its affiliates. | |
* | |
* This source code is licensed under the MIT license found in the | |
* LICENSE file in the root directory of this source tree. | |
*/ | |
import invariant from 'shared/invariant'; | |
import lowPriorityWarning from 'shared/lowPriorityWarning'; | |
import ReactNoopUpdateQueue from './ReactNoopUpdateQueue'; | |
const emptyObject = {}; | |
if (__DEV__) {Object.freeze(emptyObject); | |
} | |
/** | |
* Base class helpers for the updating state of a component. | |
*/ | |
// 帮助更新组件状态的基类 | |
function Component(props, context, updater) { | |
this.props = props; | |
// 我在工作中没用到 context,可以参考下这个://https://www.cnblogs.com/mengff/p/9511419.html | |
// 是 React 封装的全局变量 API | |
this.context = context; | |
// If a component has string refs, we will assign a different object later. | |
// 如果在组件中用了 ref="stringa" 的话,用另一个 obj 赋值 | |
this.refs = emptyObject; | |
// We initialize the default updater but the real one gets injected by the | |
// renderer. | |
// 虽然给 updater 赋了默认值,但真正的 updater 是在 renderer 中注册的 | |
this.updater = updater || ReactNoopUpdateQueue; | |
} | |
// 原型上赋了一个 flag | |
Component.prototype.isReactComponent = {}; | |
/** 使用 setState 来改变 Component 内部的变量 | |
* Sets a subset of the state. Always use this to mutate | |
* state. You should treat `this.state` as immutable. | |
* this.state 并不是立即更新的,所以在调用 this.setState 后可能 不能 拿到新值 | |
* There is no guarantee that `this.state` will be immediately updated, so | |
* accessing `this.state` after calling this method may return the old value. | |
* | |
* 不能保证 this.state 是同步的(它也不是异步的),使用回调获取最新值 | |
* | |
* There is no guarantee that calls to `setState` will run synchronously, | |
* as they may eventually be batched together. You can provide an optional | |
* callback that will be executed when the call to setState is actually | |
* completed. | |
* | |
* When a function is provided to setState, it will be called at some point in | |
* the future (not synchronously). It will be called with the up to date | |
* component arguments (state, props, context). These values can be different | |
* from this.* because your function may be called after receiveProps but before | |
* shouldComponentUpdate, and this new state, props, and context will not yet be | |
* assigned to this. | |
* | |
* @param {object|function} partialState Next partial state or function to | |
* produce next partial state to be merged with current state. | |
* @param {?function} callback Called after state is updated. | |
* @final | |
* @protected | |
*/ | |
// 更新 Component 内部变量的 API,// 也是开发中非常常用且重要的 API | |
// https://www.jianshu.com/p/7ab07f8c954c | |
// https://www.jianshu.com/p/c19e259870a5 | |
//partialState:要更新的 state,可以是 Object/Function | |
//callback:setState({xxx},callback) | |
Component.prototype.setState = function(partialState, callback) { | |
// 判断 setState 中的 partialState 是否符合条件,// 如果不符合则抛出 Error | |
invariant( | |
typeof partialState === 'object' || | |
typeof partialState === 'function' || | |
partialState == null, | |
'setState(...): takes an object of state variables to update or a' + | |
'function which returns an object of state variables.', | |
); | |
// 重要!state 的更新机制 | |
// 在 react-dom 中实现,不在 react 中实现 | |
this.updater.enqueueSetState(this, partialState, callback, 'setState'); | |
}; | |
/** | |
* Forces an update. This should only be invoked when it is known with | |
* certainty that we are **not** in a DOM transaction. | |
* | |
* 在 Component 的深层次改变但未调用 setState 时,使用该方法 | |
* | |
* You may want to call this when you know that some deeper aspect of the | |
* component's state has changed but `setState` was not called. | |
* | |
* forceUpdate 不调用 shouldComponentUpdate 方法,* 但会调用 componentWillUpdate 和 componentDidUpdate 方法 | |
* | |
* This will not invoke `shouldComponentUpdate`, but it will invoke | |
* `componentWillUpdate` and `componentDidUpdate`. | |
* | |
* @param {?function} callback Called after update is complete. | |
* @final | |
* @protected | |
*/ | |
// 强制 Component 更新一次,无论 props/state 是否更新 | |
Component.prototype.forceUpdate = function(callback) {this.updater.enqueueForceUpdate(this, callback, 'forceUpdate'); | |
}; |
解析:
(1)Component()
本质是一个类:
class Component {constructor(props, context, updater){ | |
this.props = props | |
this.context = context | |
this.refs = emptyObject | |
this.updater = updater || ReactNoopUpdateQueue | |
} | |
} |
(2)setState()
是 Component 原型上的方法,其 本质 是调用 ReactNoopUpdateQueue.js
中的 enqueueSetState()
方法,之后的文章会分析 enqueueSetState()
的,不要急
(3)forceUpdate()
同(2)
(4)我以为 React.Component()
里面实现 componentWillMount()
、render()
等内部方法,其实并没有。
React.Component()
只涉及了props
/context
/refs
/updater
/isReactComponent
/setState
/forceUpdate
,其他均没有自己实现。
二、PureComponent
GitHub:
https://github.com/AttackXiaoJinJin/reactExplain/blob/master/react16.8.6/packages/react/src/ReactBaseClasses.js
什么是 PureComponent:
可以看下这篇文章的第一点:小知识 11 点(2018.9.4):
复用性强的组件:如果一个组件的渲染只依赖于外界传进去的 props 和自己的 state,而并不依赖于其他的外界的任何数据,也就是说像纯函数一样,给它什么,它就吐出(渲染)什么出来。这种组件的复用性是最强的。即 Pure Component 或称 Dumb Component。
用法:
class A extends React.PureComponent {// 同 React.Component() | |
} |
源码:
function ComponentDummy() {} | |
//ComponentDummy 的原型 继承 Component 的原型 | |
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; | |
} | |
//PureComponent 是继承自 Component 的, 下面三行就是在继承 Component | |
// 将 Component 的方法拷贝到 pureComponentPrototype 上 | |
// 用 ComponentDummy 的原因是为了不直接实例化一个 Component 实例,可以减少一些内存使用 | |
const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy()); | |
//PureComponent.prototype.constructor = PureComponent | |
pureComponentPrototype.constructor = PureComponent; | |
// Avoid an extra prototype jump for these methods. | |
// 避免多一次原型链查找, 因为上面两句已经让 PureComponent 继承了 Component | |
// 下面多写了一句 Object.assign(),是为了避免多一次原型链查找 | |
// Object.assign 是浅拷贝,// 将 Component.prototype 上的方法都复制到 PureComponent.prototype 上 | |
// 也就是 pureComponent 的原型上 | |
// 详细请参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign | |
Object.assign(pureComponentPrototype, Component.prototype); | |
// 唯一的区别就是在原型上添加了 isPureReactComponent 属性去表示该 Component 是 PureComponent | |
pureComponentPrototype.isPureReactComponent = true; | |
export {Component, PureComponent}; |
解析:
(1)重点看最后三行做了什么:(减少内存消耗,减少原型链查找次数)
① const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy())
新建了空方法 ComponentDummy
,并继承Component
的原型;PureComponent.prototype
等于 ComponentDummy
的实例
这样做的目的是:
如果让 PureComponent.prototype
直接等于 Component
的实例对象的话(继承原型),会多继承 Component
的constructor
,但是 PureComponent
已经有自己的 constructor
了,这样就会多消耗一些内存。
所以会新建 ComponentDummy
,只继承Component
的原型,不包括constructor
,以此来节省内存。
② pureComponentPrototype.constructor = PureComponent
原型的 constructor
等于自身,覆盖掉 Component.prototype
的constructor
(Component)
①、② 就是让 PureComponent
继承 Component
,那么为什么还要多写一句Object.assign(pureComponentPrototype, Component.prototype)
呢?
③ PureComponent
的 prototype
浅拷贝 Component
的prototype
的所有属性
不写 ③ 的话:
pureComponentPrototype.__proto__=== ComponentDummy.prototype //true | |
// 也就是 | |
PureComponent.prototype.__proto__=== Component.prototype //true |
这样就多了一层隐式原型的查找,为了减少一次原型链查找,所以写了
Object.assign(pureComponentPrototype, Component.prototype)
这样的话:Component.prototype
中的方法在 PureComponent.prototype
中都有,无需再从 __proto__
上查找了。
(2)pureComponentPrototype.isPureReactComponent = true
在 ReactFiberClassComponent.js
中,有对 isPureReactComponent
的判断:
if (ctor.prototype && ctor.prototype.isPureReactComponent) { | |
return (!shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState) | |
); | |
} |
注意:(重要)
(1)整个 React
中判断 Component
类 是否需要更新,只有两个地方:
一 是看有没有 shouldComponentUpdate
方法
二 就是 ReactFiberClassComponent.js
中的 checkShouldComponentUpdate()
中对 PureComponent
的判断
(2)PureComponent
与 Component
唯一的区别:PureComponent
是自带了一个简单的 shouldComponentUpdate
来优化更新机制的。
(完)