高級 Vue 組件模式 (7)

07 使用 State Initializers

目標

到目前爲止,僅從 toggle 組件自身的角度來看,它已經能夠知足大多數的業務場景了。但咱們會發現一個問題,就是當前 toggle 組件的狀態對於調用者來講,徹底是黑盒狀態,即調用者沒法初始化,也沒法更改組件的開關狀態,這在一些場景沒法知足需求。vue

對於沒法初始化開關狀態的問題,卻是很好解決,咱們能夠在 toggle 組件聲明一個 prop 屬性 on 來表明組件的默認開關狀態,同時在 mounted 生命週期函數中將這個默認值同步到組件 data 相應的屬性中去。git

對於沒法更改開關狀態的問題,彷佛沒法簡單經過聲明一個 prop 屬性的方式來解決,而且若是咱們指望的更改邏輯是異步的話,一樣沒法知足。github

所以這篇文章着重來解決這兩個問題:app

  • toggle 組件可以支持開關狀態的初始化功能
  • toggle 組件可以提供一個 reset 方法以供重置開關狀態
  • 重置開關狀態能夠以異步的方式進行

實現

初始化開關狀態

爲了使 toggle 組件可以支持默認狀態的傳入,咱們採用聲明 prop 屬性的方式,以下:框架

on: {
  type: Boolean,
  default: false
}

以後在其 mounted 生命週期對開關狀態進行同步,以下:異步

mounted() {
    this.status.on = this.on;
  }

這樣當咱們指望 toggle的狀態進行渲染時,能夠這樣調用組件:async

<toggle :on="true" @toggle="onToggle">
  ...
</toggle>

重置開關狀態

爲了可以從外部更改 toggle 組件的開關狀態,咱們能夠在組件內部聲明一個觀測 on prop 屬性的監聽器,好比:函數

watch: {
  on(val){
    // do something...
  }
}

但若是這麼作,會存在一個問題,即目標中關於開關狀態的更改邏輯的編寫者是組件調用者,而 watch 函數的編寫者是組件實現者,因爲實現者沒法預知調用者更改狀態的邏輯,因此使用 watch 是沒法知足條件的。this

讓咱們換一個角度來思考問題,既然實現者沒法預知調用者的邏輯,何不把重置開關狀態的邏輯所有交由調用者來實現?別忘了 Vue 組件也是能夠傳入 Function 類型的 prop 屬性的,以下:spa

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;
},
...

運行效果以下:

clipboard.png

支持異步重置

在實現同步重置的基礎上,實現異步重置十分簡單,一般狀況下,處理異步較好的方式是使用 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 秒後,重置爲狀態:

clipboard.png

成果

你能夠下面的連接來看看這個組件的實現代碼以及演示:

總結

Function 類型的 prop 屬性在一些狀況下很是有用,好比文章中說起的狀態初始化,這實際上是工廠模式的一種體現,在其餘的框架中也有體現,好比 React 中,HOC 中說起的 render props 就是一種比較具體的應用,Angular 在聲明具備循環依賴的 Module 時,能夠經過 () => Module 的方式進行聲明等等。

目錄

github gist

關注公衆號 全棧101,只談技術,不談人生

clipboard.png

相關文章
相關標籤/搜索