一、React.Component()
用法:
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
什麼是 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
來優化更新機制的。
(完)