07 使用 State Initializers
目標
到目前爲止,僅從 toggle
組件自身的角度來看,它已經可以滿足大多數的業務場景了。但我們會發現一個問題,就是當前 toggle
組件的狀態對於調用者來說,完全是黑盒狀態,即調用者無法初始化,也無法更改組件的開關狀態,這在一些場景無法滿足需求。
對於無法初始化開關狀態的問題,倒是很好解決,我們可以在 toggle
組件聲明一個 prop 屬性 on
來代表組件的默認開關狀態,同時在 mounted
生命週期函數中將這個默認值同步到組件 data 相應的屬性中去。
對於無法更改開關狀態的問題,似乎無法簡單通過聲明一個 prop
屬性的方式來解決,並且如果我們期望的更改邏輯是異步的話,同樣無法滿足。
因此這篇文章着重來解決這兩個問題:
-
toggle
組件能夠支持開關狀態的初始化功能 -
toggle
組件能夠提供一個reset
方法以供重置開關狀態 - 重置開關狀態可以以異步的方式進行
實現
初始化開關狀態
爲了使 toggle
組件能夠支持默認狀態的傳入,我們採用聲明 prop 屬性的方式,如下:
on: {
type: Boolean,
default: false
}
之後在其 mounted
生命週期對開關狀態進行同步,如下:
mounted() {
this.status.on = this.on;
}
這樣當我們期望 toggle
以開的狀態進行渲染時,可以這樣調用組件:
<toggle :on="true" @toggle="onToggle">
...
</toggle>
重置開關狀態
爲了能夠從外部更改 toggle
組件的開關狀態,我們可以在組件內部聲明一個觀測 on
prop 屬性的監聽器,比如:
watch: {
on(val){
// do something...
}
}
但如果這麼做,會存在一個問題,即目標中關於開關狀態的更改邏輯的編寫者是組件調用者,而 watch 函數的編寫者是組件實現者,由於實現者無法預知調用者更改狀態的邏輯,所以使用 watch 是無法滿足條件的。
讓我們換一個角度來思考問題,既然實現者無法預知調用者的邏輯,何不把重置開關狀態的邏輯全部交由調用者來實現?別忘了 Vue 組件也是可以傳入 Function 類型的 prop
屬性的,如下:
onReset: {
type: Function,
default: () => this.on
},
這樣就將提供重置狀態的邏輯暴露給了組件調用者,當然,如果調用者沒有提供相關重置邏輯,組件內部會自動降級爲使用 on
屬性來作爲重置的狀態值。
組件內部額外聲明一個 reset 方法,在其內部重置當前的開關狀態,如下:
reset(){
this.status.on = this.onReset(this.status.on)
this.$emit("reset", this.status.on)
}
這裏會首先以當前開關狀態爲參數,調用 onReset
方法,再將返回值賦值給當前狀態,並觸發一個 reset
事件以供父組件訂閱。
之後在 app 組件中,可以按如下方式傳入 onReset
函數,並編寫具體的重置邏輯:
// template
<toggle :on="false" @toggle="onToggle" :on-reset="resetToTrue">
...
</toggle>
// script
...
resetToTrue(on) {
return true;
},
...
運行效果如下:
支持異步重置
在實現同步重置的基礎上,實現異步重置十分簡單,通常情況下,處理異步較好的方式是使用 Promise,使用 callback 也可以,使用 Observable 也是不錯的選擇,這裏我們選擇 Promise。
由於要同時處理同步和異步兩種情況,只需把同步情況視爲異步情況即可,比如以下兩種情況在效果上是等價的:
// sync
this.status.on = this.onReset(this.status.on)
// async
Promise.resolve(this.onReset(this.status.on))
.then(on => {
this.status.on = on
})
而 onReset
函數如果返回的是一個 Promise
實例的話,Promise.resolve
也會正確解析到當它爲 fullfill
狀態的值,這樣關於 reset
方法我們改版如下:
reset(){
Promise.resolve(this.onReset(this.status.on))
.then(on => {
this.status.on = on
this.$emit("reset", this.status.on)
})
}
在 app 組件中,可以傳入一個異步的重置邏輯,這裏就不貼代碼了,直接上一個運行截圖,組件會在點擊重置按鈕後 1 秒後,重置爲開狀態:
成果
你可以下面的鏈接來看看這個組件的實現代碼以及演示:
總結
Function 類型的 prop 屬性在一些情況下非常有用,比如文章中提及的狀態初始化,這其實是工廠模式的一種體現,在其他的框架中也有體現,比如 React 中,HOC 中提及的 render props
就是一種比較具體的應用,Angular 在聲明具有循環依賴的 Module 時,可以通過 () => Module
的方式進行聲明等等。