1、React.Component() html
用法:git
class A extends React.Component { constructor(props){ super(props) this.state={ } } componentWillMount(){ } render() { return { } } }
源碼:github
/** * 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()
本質是一個類:dom
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)
ide
(4)我覺得React.Component()
裏面實現componentWillMount()
、render()
等內部方法,其實並無。函數
React.Component()
只涉及了props
/context
/refs
/updater
/isReactComponent
/setState
/forceUpdate
,其餘均沒有本身實現。oop
2、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
來優化更新機制的。
(完)