閱讀源碼成了今年的學習目標之一,在選擇 Vue 和 React 之間,我想先閱讀 React 。 在考慮到讀哪一個版本的時候,我想先接觸到源碼早期的思想可能會更輕鬆一些,最終我選擇閱讀
0.3-stable
。 那麼接下來,我將從幾個方面來解讀這個版本的源碼。javascript
那麼關於生命週期, React 當中生命週期有 2 個。java
一個是組件的生命週期 _lifeCycleState
,另外一個是複合生命週期 _compositeLifeCycleState
用於複合組件。node
那麼關於組件的生命週期:函數
// core/ReactComponent.js
/** * Every React component is in one of these life cycles. */
var ComponentLifeCycle = keyMirror({
/** * Mounted components have a DOM node representation and are capable of * receiving new props. */
// 已掛載
MOUNTED: null,
/** * Unmounted components are inactive and cannot receive new props. */
// 未掛載
UNMOUNTED: null
});
複製代碼
那麼咱們來觀測到, ReactComponent
和 ReactCompositeComponent
關於 ComponentLifeCycle
的狀態變化:學習
// core/ReactComponent.js
var ReactComponent = {
Mixin: {
getDOMNode: function() {
// 獲取 DOM 節點時,組件必須爲已掛載
invariant(
this._lifeCycleState === ComponentLifeCycle.MOUNTED,
'getDOMNode(): A component must be mounted to have a DOM node.'
);
},
construct: function(initialProps, children) {
// All components start unmounted.
// 實例化時,組件爲未掛載
this._lifeCycleState = ComponentLifeCycle.UNMOUNTED;
},
mountComponent: function(rootID, transaction) {
// 掛載組件前檢查組件應爲未掛載
invariant(
this._lifeCycleState === ComponentLifeCycle.UNMOUNTED,
'mountComponent(%s, ...): Can only mount an unmounted component.',
rootID
);
// 掛載完後更新組件生命週期狀態
this._lifeCycleState = ComponentLifeCycle.MOUNTED;
// Effectively: return '';
},
unmountComponent: function() {
// 卸載前檢查組件應爲已掛載
invariant(
this._lifeCycleState === ComponentLifeCycle.MOUNTED,
'unmountComponent(): Can only unmount a mounted component.'
);
// 卸載完後更新組件生命週期狀態
this._lifeCycleState = ComponentLifeCycle.UNMOUNTED;
},
receiveProps: function(nextProps, transaction) {
// 更新 props 時,組件生命週期應爲已掛載
invariant(
this._lifeCycleState === ComponentLifeCycle.MOUNTED,
'receiveProps(...): Can only update a mounted component.'
);
},
}
};
複製代碼
// core/ReactCompositeComponent.js
var ReactCompositeComponentMixin = {
mountComponent: function(rootID, transaction) {
ReactComponent.Mixin.mountComponent.call(this, rootID, transaction);
// Unset `this._lifeCycleState` until after this method is finished.
this._lifeCycleState = ReactComponent.LifeCycle.UNMOUNTED;
// ...
this._lifeCycleState = ReactComponent.LifeCycle.MOUNTED;
},
replaceState: function(completeState) {
var compositeLifeCycleState = this._compositeLifeCycleState;
// 更新 state 時,組件生命週期必須爲已掛載,或者複合組件生命週期爲掛載中
invariant(
this._lifeCycleState === ReactComponent.LifeCycle.MOUNTED ||
compositeLifeCycleState === CompositeLifeCycle.MOUNTING,
'replaceState(...): Can only update a mounted (or mounting) component.'
);
},
_bindAutoBindMethod: function(method) {
function autoBound(a, b, c, d, e, tooMany) {
// 使用綁定上下文的方法時,組件生命週期必須爲已掛載
if (component._lifeCycleState === ReactComponent.LifeCycle.MOUNTED) {
return method.call(component, a, b, c, d, e);
}
}
}
};
複製代碼
那麼接下來,咱們來看看複合生命週期以及其狀態變化:this
// core/ReactCompositeComponent.js
/** * `ReactCompositeComponent` maintains an auxiliary life cycle state in * `this._compositeLifeCycleState` (which can be null). * * This is different from the life cycle state maintained by `ReactComponent` in * `this._lifeCycleState`. */
var CompositeLifeCycle = keyMirror({
/** * Components in the process of being mounted respond to state changes * differently. */
// 掛載中
MOUNTING: null,
/** * Components in the process of being unmounted are guarded against state * changes. */
// 卸載中
UNMOUNTING: null,
/** * Components that are mounted and receiving new props respond to state * changes differently. */
// 更新 props
RECEIVING_PROPS: null,
/** * Components that are mounted and receiving new state are guarded against * additional state changes. */
// 更新 state
RECEIVING_STATE: null
});
var ReactCompositeComponentMixin = {
construct: function(initialProps, children) {
// 實例化時置空複合生命週期
this._compositeLifeCycleState = null;
},
mountComponent: function(rootID, transaction) {
// 掛載前設置複合生命週期爲掛載中
this._compositeLifeCycleState = CompositeLifeCycle.MOUNTING;
// 掛載完成後置空複合生命週期
// Done with mounting, `setState` will now trigger UI changes.
this._compositeLifeCycleState = null;
},
unmountComponent: function() {
// 卸載開始時設置複合生命週期爲卸載中
this._compositeLifeCycleState = CompositeLifeCycle.UNMOUNTING;
if (this.componentWillUnmount) {
this.componentWillUnmount();
}
// 通過生命週期函數 componentWillUnmount 後,置空複合生命週期
this._compositeLifeCycleState = null;
},
receiveProps: function(nextProps, transaction) {
// 更新 props 時設置複合生命週期爲更新 props
this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_PROPS;
if (this.componentWillReceiveProps) {
this.componentWillReceiveProps(nextProps, transaction);
}
// 執行生命週期函數 componentWillReceiveProps 後,設置複合生命週期爲更新 state
this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_STATE;
// ...
// 操做 state 更新相關後,置空複合生命週期
this._compositeLifeCycleState = null;
},
replaceState: function(completeState) {
// 更新 state
var compositeLifeCycleState = this._compositeLifeCycleState;
// 僅限生命週期爲掛載中或者複合生命週期爲掛載中能夠更新 state
invariant(
this._lifeCycleState === ReactComponent.LifeCycle.MOUNTED ||
compositeLifeCycleState === CompositeLifeCycle.MOUNTING,
'replaceState(...): Can only update a mounted (or mounting) component.'
);
// 僅限複合生命週期不爲更新 state 或者不爲卸載中
invariant(
compositeLifeCycleState !== CompositeLifeCycle.RECEIVING_STATE &&
compositeLifeCycleState !== CompositeLifeCycle.UNMOUNTING,
'replaceState(...): Cannot update while unmounting component or during ' +
'an existing state transition (such as within `render`).'
);
this._pendingState = completeState;
// Do not trigger a state transition if we are in the middle of mounting or
// receiving props because both of those will already be doing this.
// 若是咱們正在安裝或接收道具,請不要觸發狀態轉換,由於這兩個道具都已經在進行此操做了。
// 僅限複合生命週期不爲掛載中 或者不爲更新 props
if (compositeLifeCycleState !== CompositeLifeCycle.MOUNTING &&
compositeLifeCycleState !== CompositeLifeCycle.RECEIVING_PROPS) {
// 更新複合生命週期爲更新 state
this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_STATE;
// ... 執行更新相關操做
// 置空複合生命週期
this._compositeLifeCycleState = null;
}
},
};
複製代碼
那麼到此,實現生命週期功能。那麼讓咱們來看看那些生命週期的鉤子都在哪裏:spa
// core/ReactCompositeComponent.js
var ReactCompositeComponentInterface = {
mixins: SpecPolicy.DEFINE_MANY,
props: SpecPolicy.DEFINE_ONCE,
getInitialState: SpecPolicy.DEFINE_ONCE,
render: SpecPolicy.DEFINE_ONCE,
// ==== Delegate methods ====
// **一下內容爲 Google 翻譯**
// 最初建立組件並即將安裝時調用。 這可能有反作用,但必須在 `componentWillUnmount` 中清除此方法建立的任何外部訂閱或數據。
componentWillMount: SpecPolicy.DEFINE_MANY,
// 在組件已裝入並具備DOM表示形式時調用。 可是,沒法保證DOM節點位於文檔中。 在第一次裝入(初始化和渲染)組件時,將此做爲操做DOM的機會。
componentDidMount: SpecPolicy.DEFINE_MANY,
// 在組件接收新道具以前調用。 使用此做爲經過使用 `this.setState` 更新狀態來對prop轉換做出反應的機會。 目前的道具是經過 `this.props` 訪問的。
// 注意:沒有等效的 `componentWillReceiveState` 。傳入的道具轉換可能會致使狀態改變,但狀況偏偏相反。若是你須要它,你可能正在尋找 `componentWillUpdate` 。
componentWillReceiveProps: SpecPolicy.DEFINE_MANY,
// 在決定是否應該因接收新的道具和狀態而更新組件時調用。 當您肯定轉換到新的道具和狀態不須要更新組件時,能夠將此做爲 `return false` 的機會。
shouldComponentUpdate: SpecPolicy.DEFINE_ONCE,
// 因爲從 `this.props` 和 `this.state` 轉換爲 `nextProps` 和 `nextState` 而致使組件即將更新時調用。使用此做爲在更新發生以前執行準備的機會。
// 注意:您**不能**在此方法中使用 `this.setState()` 。
componentWillUpdate: SpecPolicy.DEFINE_MANY,
// 更新組件的DOM表示時調用。 將此做爲在更新組件時對DOM進行操做的機會。
componentDidUpdate: SpecPolicy.DEFINE_MANY,
// 當組件即將從其父組件中刪除並銷燬其DOM表示時調用。 使用此做爲釋聽任何外部資源的機會。 注意:沒有 `componentDidUnmount` ,由於您的組件將被該點銷燬。
componentWillUnmount: SpecPolicy.DEFINE_MANY,
// 到此
updateComponent: SpecPolicy.OVERRIDE_BASE
};
複製代碼
來看一下生命週期圖:翻譯