在第七篇文章中,咱們對 toggle
組件進行了重構,使父組件可以傳入開關狀態的初始值,同時還能夠傳入自定義的狀態重置邏輯。雖然父組件擁有了改變 toggle
組件內部狀態的途徑,可是若是進一步思考的話,父組件並無絕對的控制權。在一些業務場景,咱們指望父組件對於子組件的狀態,擁有絕對的控制權。vue
熟悉 React 的讀者必定不會對智能組件(Smart Component)和木偶組件(Dump Component)感到陌生。對於後者,其父組件必定對其擁有絕對控制權,由於它內部沒有狀態,渲染邏輯徹底取決於父組件所傳 props 的值。而對於前者則相反,因爲組件內部會有本身的狀態,它內部的渲染邏輯由父組件所傳 props 與其內部狀態共同決定。git
這篇文章將着重解決這個問題,若是可以使一個智能組件的狀態變得可控,即:github
toggle
組件的開關狀態應該徹底由 prop 屬性 on 的值決定toggle
組件的開關狀態降級爲內部管理額外地,咱們還將實現一個小需求,toggle
組件的開關狀態至多切換四次,若是超過四次,則需點擊重置後,纔可以從新對開關切換狀態進行切換。this
因爲 toggle
組件爲一個智能組件,咱們須要提供一個斷定它是否受控的方式。很簡單,由目標中的第一點可知,當父組件傳入了 on
屬性後,toggle
處於被控制的狀態,不然則沒有,因而能夠利用 Vue 組件的 computed
特性,聲明一個 isOnControlled
計算屬性,以下:spa
computed: { isOnControlled() { return this.on !== undefined; } }
其內部邏輯很簡單,就是斷定 prop 屬性 on
的值是否爲 undefined
,若是是,則未被父組件控制,反之,則被父組件控制。code
因爲要知足目標中說起的第二點,關於 prop 屬性 on 的聲明,咱們要作出一些調整,以下:component
on: { type: Boolean, default: undefined },
就是簡單地將默認值,由 false
改成了 undefined
,這麼作的緣由是由於,按照以前的寫法,若是 on
未由父組件傳入,則默認值爲 false
,那麼 toggle
組件會認爲父組件實際傳入了一個值爲 false
的 on
屬性,所以會將其內部的開關狀態控制爲關,而非降級爲內部管理開關狀態。blog
以前的實現中,經過 scope-slot 注入插槽的狀態徹底取決於組件內部 status
的值,咱們須要改變狀態的注入邏輯。當組件受控時,其開關狀態應該與 prop 屬性保持一致,反之,則和原來同樣。所以編寫一個叫作 controlledStatus
的計算屬性:事件
controlledStatus() { return this.isOnControlled ? { on: this.on } : this.status; }
這裏利用了以前聲明的 isOnControlled
屬性來判斷當前組件是否處於受控狀態。以後相應地把模板中開關狀態的注入邏輯也進行更改:ip
<slot :status="controlledStatus" :toggle="toggle" :reset="reset"></slot>
相應地,除了開關狀態的注入邏輯,toggle
方法和 reset
方法的注入邏輯也須要更改,至於爲何,就交由讀者自行思考得出答案吧,這裏簡單羅列實現代碼,以供參考:
// toggle 方法 toggle() { if (this.isOnControlled) { this.$emit("toggle", !this.on); } else { this.status.on = !this.status.on; this.$emit("toggle", this.status.on); } } // reset 方法 reset() { if (this.isOnControlled) { Promise.resolve(this.onReset(!this.on)).then(on => { this.$emit("reset", on); }); } else { Promise.resolve(this.onReset(this.status.on)).then(on => { this.status.on = on || false; this.$emit("reset", this.status.on); }); } }
整體上的思路是,若是組件受控,則傳入回調方法中的開關狀態參數,是在觸發相應事件後,由 prop 屬性 on 得出的組件在下一時刻,應當處於的狀態。
這麼說可能有點繞,換句話說就是,當組件狀態發生更改時,若是當前的 on 屬性爲 true(開關狀態爲開),則組件本該處於關的狀態,但因爲組件受控,則它內部不能直接將開關狀態更改成關,而是依舊保持爲開,可是它會將 false(開關狀態爲關)做爲參數傳入觸發事件,這將告知父組件,當前組件的下一個狀態爲關,至於父組件是否贊成將其狀態更改成關則有父組件決定。
若是組件不受控,開關狀態由組件內部自行管理,那和以前的實現邏輯是如出一轍的,保留以前的代碼便可。
當 toggle
組件被改造後,實現這個需求就很容易了。關於實現的代碼,這裏就不進行羅列了,有興趣能夠經過在線代碼連接進行查看,十分簡單,這裏僅簡單附上一個最終的動態效果圖:
你能夠經過下面的連接來看看這個組件的實現代碼以及演示:
關於 Controlled Component 和 Uncontrolled Component 的概念,我第一次是在 React 中關於表單的介紹中接觸到的。實際工做中,大部分對於狀態可控的需求也都存在於表單組件中,之因此存在這樣的需求,是由於表單系統每每是複雜的,將其實現爲智能組件,每每內部狀態過於複雜,而若是實現爲木偶組件,代碼結構或者實現邏輯又過於繁瑣,這時若是能夠借鑑這種模式的話,每每能夠達到事半功倍的效果。
關注公衆號 全棧101,只談技術,不談人生
![]()