在hooks出現以前, setState是惟一的方式來改變組件的內部state. 本文梳理一下, setState的工做原理和執行流程.javascript
// componentjava
setState(nextState, callback) {
// 添加異步隊列 不是每次都更新
this.$updater.addCallback(callback)
this.$updater.addState(nextState)
}
複製代碼
// updaternode
getState() {
let { instance, pendingStates } = this
let { state, props } = instance
// 合併待處理狀態數組
if (pendingStates.length) {
state = { ...state }
pendingStates.forEach(nextState => {
let isReplace = _.isArr(nextState)
if (isReplace) {
nextState = nextState[0]
}
if (_.isFn(nextState)) {
// 函數方式將當即執行,它能夠得到以前合併的狀態結果
nextState = nextState.call(instance, state, props)
}
// replace state
if (isReplace) {
state = { ...nextState }
} else {
// 合併新舊狀態
state = { ...state, ...nextState }
}
})
pendingStates.length = 0
}
return state
}
複製代碼
// updater
addState(nextState) {
if (nextState) {
// 放入更新隊列
this.pendingStates.push(nextState)
// 若是當前隊列沒有工做則直接更新
if (!this.isPending) {
this.emitUpdate()
}
}
}
emitUpdate(nextProps, nextContext) {
this.nextProps = nextProps
this.nextContext = nextContext
// receive nextProps!! should update immediately
nextProps || !updateQueue.isPending
? this.updateComponent()
: updateQueue.add(this)
}
// updateQueue
add(updater) {
this.updaters.push(updater)
}
batchUpdate() {
if (this.isPending) {
return
}
this.isPending = true
let { updaters } = this
let updater
while (updater = updaters.pop()) {
updater.updateComponent()
}
this.isPending = false
}
//updater
updateComponent() {
let { instance, pendingStates, nextProps, nextContext } = this
if (nextProps || pendingStates.length > 0) {
// ...
// getState 合併全部的state的數據,一次更新
shouldUpdate(instance, nextProps, this.getState(), nextContext,
this.clearCallbacks)
}
}
function shouldUpdate(component, nextProps, nextState, nextContext, callback) {
component.forceUpdate(callback)
}
// Component
// 跳過全部生命週期執行強制更新
forceUpdate(callback) {
// 實際更新組件的函數
let { $updater, $cache, props, state, context } = this
//...
// 下面纔是重點 diff
let newVnode = renderComponent(this)
let newNode = compareTwoVnodes(vnode, newVnode, node, getChildContext(this,
parentContext))
// ...
}
複製代碼