[Vue] 跟著 Vue 闖蕩前端世界 - 06 父子組件溝通 pass props / emit event

Vue 與 React 的概念都是先針對頁面拆分組件化來進行開發,最後再將所有組件合成為我們所需的功能,而組件溝通存在許多種方式及淺在問題,開發人員可以依照需求自行搭配使用,而本篇來研究一下基本的父子上下層的基礎溝通方式。

前言


網頁應用程式單頁面就可能存在許多組件,當然有些組件的運作相當獨立,不需要與外部進行過多的溝通,但是我相信絕大部分的組件都存在著與其他組件溝通的需求;我們當然可以採用 veux 官方建議的狀態管理套件,角色如同 react 的 redux 來管理各組件之共用狀態,但勢必付出一些代價( ex.學習成本、多寫一些代碼來套用 vuex 資料流)。不可否認在大型網頁應用程式是有透過 vuex 來管理錯綜複雜狀態之必要性,但本篇先藉由一些基本的溝通方式入門,後續有機會在說明 vuex 相關的操作概念及實作方式。

 

父子組件溝通原則


為了提高組件的獨立性與重用性,父組件會透過 props 向下傳遞資料給子組件,當子組件有事情要通知父組件時會透過 emit 事件告知父組件,如此確保每個組件都是獨立在相對隔離的環境中運作,可以大幅提高組件的維護性。

ref: http://cn.vuejs.org/v2/guide/components.html#Prop

 

pass props


此屬性是作為外部傳入資料的接口,為遵循單向資料流,避免子組件無意修改了父組件資料,因此在設計上 props 是單向綁定機制。

  • 當父組件更新傳入值的時候,子組件 props 都會更新為最新值
  • 子組件不允許直接異動 props 屬性值 (主控台會發出警告訊息)
  • 子組件強行修改 props 屬性值時,由於單向資料流的特性,因此是不會異動到父組件的數值。
特別注意若傳入物件或陣列型態資料時,若子組件強行修改 props 值是會異動到父組件的資料喔!

 

建立子組件

來驗證一下上述特性,首先建立 Product Card 子組件 (ProductCard.vue),並且建立兩個 props 接受外部傳入之資料,及一個 props 接收外部傳入的方法 。

  • type : 表示資料型態 (會進行類型檢測,不符會在主控台發出警告訊息)
  • default : 設定預設值 (無傳入資料時,使用預設值)
  • required : 是否為必輸入屬性 (預設為 false)

另外有兩的 method 用來強行修改 props 屬性資料

  • 修改 string 型態的 props 屬性
  • 修改 object 型態的 props 屬性

畫面如下,提供兩個修改資料的連結供測試

其中點選 More Info 按鍵就是直接執行外部傳入的 function props ,並且傳入參數至該 function 中。

 

建立父組件

接著建立 Props Tester 父組件 (PropsTester.vue),並且使用 Product Card 子組件。

傳入三個 props (string, object and function) 至該組件中

在父組件中呈現的畫面如下,上方輸入框呈現父組件資料,皆透過 v-model 雙向綁定各資料;下方則是傳入資料後所呈現出的子組件,皆使用 v-bind 單向綁定傳入 props 的資料。

 

測試父子互動

當父組件更新傳入值的時候,子組件 props 都會更新為最新值

在子組件修改傳入  props 型態為 string 資料時,系統會發出警告,並且無法異動父組件資料

但在子組件修改傳入  props 型態為 object資料時,可是會直接異動父組件資料(雙向綁定) 喔!

最後點選 More Info 後順利執行父層透過 function props 傳入的方法,並且確實傳入變數至該方法中。

 

emit event


先前有提到父組件傳入的資料是不行被異動的,因此如果要在子組件操作該資料,並且要在資料異動時通知父組件,讓父組件去做相對應的資料處理時,就要透過 emit 事件通知父層。

 

如何在子層使用父層傳入的資料?

  • 子層內部使用,變動後無須通知父層 :
    簡單定義 data 屬性 internalMadeFrom,將傳入值 madeFrom 作為該值的初始值,後續在子組件就直接操作 internalMadeFrom 即可。
     
  • 父子共用,異動必須通知父層 :
    定義 computed 計算屬性 clonedMadeFrom,在 getter 中直接取用 props 傳入 madeFrom 資料,在 setter 中直接將異動後新值透過 emit 發送特定事件讓父層接收(可透過此機制讓父層更新 madeFrom 值,再透過 prop 傳遞新值進子組件,更新子層 madeFrom 資料,就類似雙向綁定作用),後續在子組件就直接操作 clonedMadeFrom 即可。

若選擇通知父層,因此子層對所有 madeFrom props 的操作(顯示/修改)都須調整為 clonedMadeFrom。

 

如何在父層監聽子層傳出的事件?

首先須定義出接收到子層送出資料異動事件後,所需執行的方法;在此定義 onMadeFromChanged 方法,接收子組件修改的新數值,重新更新父層 madeFrom 資料,達到雙向綁定的作用。

最後透過 v-on 在子組件上加入事件 handler,事件名稱為 mateFromChanged (子組件 emit 的事件名稱 )。

 

執行結果

此結果類似自行實做 v-model 雙向綁定機制,重點流程如下:

  1. 子組件修改 computed 的變數,觸發 setter 方法執行 emit 事件,並送出修改後的資料
  2. 父組件監聽到子組件送出的事件,更新父組件該特定傳入值 madeFrom 為子組件修改後的新資料
  3. 因父組件傳入 prop 的 madeFrom 資料變動後,子組件 props 都會更新為最新值

為何說類似實作 v-model 呢?因為如果實作 v-model 只需在組件中定義名為 value 的 props 資料,並在 value 變動時 emit 名為 input 的事件傳遞變更後 value 值即可,而父層只需透過 v-model 傳遞資料就可以達到雙向綁定的效果,是不需要自行處理 v-on:input 來更新資料喔!(因為v-model已經幫忙實作這段)

 

參考資訊


Vue 2 官方文件 - props

 

測試代碼已上傳 GitHub 中,有需要的朋友可以參考一下。
若有更好的建議或做法再請不吝指導一下囉! 感謝!

 


希望此篇文章可以幫助到需要的人

若內容有誤或有其他建議請不吝留言給筆者喔 !