Preact是React的輕量級實作,是React比較好的替代者之一,有着體積小的優點,當然與React之間一定會存在實作上的差異,本文介紹了在 `setState` 方面的差異之處。
Preact是React的輕量級實作,是React比較好的替代者之一,有着體積小的優點,當然與React之間一定會存在實作上的差異,本文介紹了在 setState 方面的差異之處。
源碼分析
首先來分析下React以及Preact在setState部分的具體實作。
(太長不看想偷懶,可以直接下翻看結論)
React
關鍵代碼:
setState 階段:
// ReactUpdateQueue.js
enqueueSetState: function(publicInstance, partialState) {
...
var queue =
internalInstance._pendingStateQueue ||
(internalInstance._pendingStateQueue = []);
queue.push(partialState);
enqueueUpdate(internalInstance);
}
可以看到React在 setState 的時候不會做任何處理,會把變更直接放到一個專門處理 state 的隊列裡供元件更新時使用。
更新階段:
// ReactCompositeComponent.js
updateComponent: function(
transaction,
prevParentElement,
nextParentElement,
prevUnmaskedContext,
nextUnmaskedContext,
) {
var inst = this._instance;
...
var willReceive = false;
var nextContext;
if (this._context === nextUnmaskedContext) {
nextContext = inst.context;
} else {
nextContext = this._processContext(nextUnmaskedContext);
willReceive = true;
}
var prevProps = prevParentElement.props;
var nextProps = nextParentElement.props;
if (prevParentElement !== nextParentElement) {
willReceive = true;
}
if (willReceive && inst.componentWillReceiveProps) {
...
inst.componentWillReceiveProps(nextProps, nextContext);
}
// 在此處才計算 nextState
var nextState = this._processPendingState(nextProps, nextContext); // 此處傳入了 nextProps
var shouldUpdate = true;
if (!this._pendingForceUpdate) {
if (inst.shouldComponentUpdate) {
...
shouldUpdate = inst.shouldComponentUpdate(
nextProps,
nextState,
nextContext,
);
} else {
if (this._compositeType === CompositeTypes.PureClass) { // 敲黑闆,知識點 —— 如果你的元件沒實作shouldComponentUpdate,那麼把React.Component 換成 React.PureComponent 可以獲得基礎版優化,提高性能。
shouldUpdate =
!shallowEqual(prevProps, nextProps) ||
!shallowEqual(inst.state, nextState); // 淺比較,可以抄去自己改成屬性黑/白名單版
}
}
}
...
}
// ReactCompositeComponent.js
_processPendingState: function(props, context) { // props: nextProps
var inst = this._instance;
var queue = this._pendingStateQueue;
var replace = this._pendingReplaceState;
this._pendingReplaceState = false;
this._pendingStateQueue = null;
if (!queue) {
return inst.state;
}
if (replace && queue.length === 1) {
return queue[0];
}
var nextState = Object.assign({}, replace ? queue[0] : inst.state);
for (var i = replace ? 1 : 0; i < queue.length; i++) {
var partial = queue[i];
Object.assign(
nextState,
typeof partial === 'function'
? partial.call(inst, nextState, props, context) // nextProps
: partial,
);
}
return nextState;
}
通過上面元件更新的流程代碼可以看到:
- 在 updateComponent 中,在 componentWillReceiveProps 之後才會計算 nextState,是以在 componentWillReceiveProps 中 setState 是可以在當次更新中生效的。
- 在 _processPendingState 會對隊列裡的 state 進行疊加,如果修改是函數方式,此處傳入的state參數是 nextState,props 是 nextProps。
Preact
// component.js
setState(state, callback) {
let s = this.state;
if (!this.prevState) this.prevState = extend({}, s);
extend(s, typeof state==='function' ? state(s, this.props) : state);
if (callback) (this._renderCallbacks = (this._renderCallbacks || [])).push(callback);
enqueueRender(this);
}
實作的簡單粗暴,在 setState 的時候就進行了合并,會立即改寫 this.state,在第一次 setState 時會保留 state 狀态到 prevState。由于是立即合并state,如果入參state是函數,props 将隻是目前 this.props。
export function renderComponent(component, opts, mountAll, isChild) {
...
previousProps = component.prevProps || props,
previousState = component.prevState || state,
previousContext = component.prevContext || context,
...
// if updating
if (isUpdate) {
component.props = previousProps;
component.state = previousState;
component.context = previousContext;
if (opts!==FORCE_RENDER
&& component.shouldComponentUpdate
&& component.shouldComponentUpdate(props, state, context) === false) {
skip = true;
}
else if (component.componentWillUpdate) {
component.componentWillUpdate(props, state, context);
}
component.props = props;
component.state = state;
component.context = context;
}
...
}
在更新流程前提取了舊 state,shouldComponentUpdate、componentWillUpdate 之後還原回新值,是以在 shouldComponentUpdate 生命周期中,this.props 将擷取的是 prevProps,這裡與 React 的邏輯并不一緻。
劃重點
相同點:
- 在 componentWillReceiveProps 中 setState 都會應用到 nextState。
- 在 shouldComponentUpdate 中 setState 都不會應用到 nextState,但是可以直接操作傳入的 nextState。
不同點:
- React下 setState 的值不會立即生效,會一直積累到 componentWillReceiveProps,在此之後會進行合并,并提供給後續生命周期。而Preact下 setState 會立即反映到 this.state,但是,在更新元件的生命周期到 render 前(eg: shouldComponentUpdate), this.state 将會是 prevState。
- shouldComponentUpdate 階段 setState 雖然不會影響到最終 state 的值,但是Preact下會影響 this.state 的值,比如之後 componentWillUpdate 中的 this.state, 總之此階段不要 setState 反正也沒用。
- setState 如果使用函數修改,Preact下傳入的 props 将會是 prevProps,而React中是 nextProps,在 componentWillReceiveProps 中 setState 時要注意。
總結
如果你寫的工程需要同時相容React及Preact的話:
- 不要利用React下 setState 在同一次元件更新執行前 state 不立即更新的特性,注意多個 setState 之間是否影響,必要時手動儲存舊值。
- 在元件更新生命周期内,除 componentWillReceiveProps 之外不要使用 setState,提供了 nextState 的生命周期,可以直接修改 nextState。
- 盡量避免使用 setState 函數修改方式,在 componentWillReceiveProps 中使用時,使用生命周期中的 prevProps(this.props) 和 nextProps。
p.s: antd-mobile 2.0正式版已釋出,同時相容react、preact,輕量、快速、易用的移動端元件庫,等你來用~ 【傳送門】
作者:SuperEVO
出處:http://www.cnblogs.com/zhang740/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利.