1. 程式人生 > >Vue 父子元件間的通訊

Vue 父子元件間的通訊

前言 在 Vue 專案中父子元件的通訊是非常常見的,最近做專案的時候發現對這方面的知識還不怎麼熟練,在這邊做一下筆記,系統學習一下吧。

1 父元件傳值給子元件

1.1 傳值寫法

父元件傳值給子元件,這個就比較常見了,直接用 props 就可以了。但是就算是 props 子元件那邊也有三種寫法,如下面程式碼所示:
父元件

<!-- 兩種情況 -->
<!--靜態傳值-->
<child name="xhm"></child>
<!--動態傳值-->
<child :name="userName"></
child
>

子元件

// 1 簡單粗暴就給個名稱的情況
props:['name'],
// 2 給個名稱順便指定個型別,如果父元件傳遞過來的值型別不對的話就會報錯
props:{
    name:String
},
// 3 給個名稱不僅指定了型別,還順便送了個預設值,當父元件傳個空過來或者啥都沒傳過來的時候就用預設值了
props: {
  name: {
    type: String,
    default: 'xhm',
  }
},

注意一下的話,如果是陣列或者是物件要預設值的話,直接設定預設陣列或者預設物件會報錯,需要用工廠函式返回,如下:

props: {
arr:{ type:Array, default:()=>{ return [1,2,3] } } }, // 物件也是和上面一個用工廠函式

1.2 子元件使用父元件的值

由於單向資料流的限制,我們不能直接在子元件中修改 props 的值,當我們修改的時候會報錯,官方的說法是:

所有的 prop 都使得其父子 prop 之間形成了一個單向下行繫結:父級 prop 的更新會向下流動到子元件中,但是反過來則不行。這樣會防止從子元件意外改變父級元件的狀態,從而導致你的應用的資料流向難以理解。

額外的,每次父級元件發生更新時,子元件中所有的 prop 都將會重新整理為最新的值。這意味著你不應該在一個子元件內部改變 prop。如果你這樣做了,Vue 會在瀏覽器的控制檯中發出警告。

所以啊,如果你不只是想在子元件中簡單的渲染父元件傳過來的值的話,那麼可以用下面的兩種方法。

  1. 這個 prop 用來傳遞一個初始值;這個子元件接下來希望將其作為一個本地的 prop 資料來使用。在這種情況下,最好定義一個本地的 data 屬性並將這個 prop 用作其初始值:
props: ['name'],
data() {
  return {
    userName:this.name,
  };
},
  1. 這個 prop 以一種原始的值傳入且需要進行轉換。在這種情況下,最好使用這個 prop 的值來定義一個計算屬性:
props: ['name'],
computed: {
  userName(){
    return this.firstName + this.name
  }
},

2 子元件傳值給父元件

雖然我們說是要單向資料流,但是很多時候,我們在子元件改變了某些值之後,還是要反饋給父元件,讓父元件做一下修改,那麼這個時候就要想著子元件向父元件傳值啦。有下面這麼兩種方式

2.1 emit 方法

這個基本都是用 emit 來傳遞了,用法直接看程式碼吧:
子元件

// props:['name]

// methods 裡
update(){
  this.$emit('update','ljy');
  // 第一個引數:事件名,第二個引數:傳遞給事件方法的引數
}

父元件


/* template 裡面的程式碼,監聽子元件裡面的 update 事件,呼叫父元件的 childUpdate 方法

 <child :name="userName" @update="childUpdate"></child>

*/
// methods
chidlUpdate(val){
  // val 引數就是子元件傳遞過來的資料
  this.userName = val;
}

雖然這樣是可以實現子元件向父元件傳值,但是寫多一個方法感覺很煩,所以 vue 官方高出了一個 以 update:myPropName 的模式觸發事件。,這個是啥,舉個例子,我們的子元件中有一個 name 的 props,我們用下面這個形式通知父元件

this.$emit('update:name', newName)
// this.$emit('update:props中的變數名', 新的值)

然後父元件可以監聽那個事件並根據需要更新一個本地的資料屬性:

<child :name="userName" @update:name="userName = $event"></child>

這樣當我們在子元件觸發那個修改的方法的時候,父元件的 userName 變數就會更新為 newName了,然後為了方便起見,官方提供了一個縮寫,即 .sync 修飾符。看程式碼吧:

<child :name.sync="userName"></child>

上面的程式碼和前面的程式碼是一個效果,是不是方便了很多。舒服了吧。

2.2 利用淺拷貝(不推薦)

這個的話是針對於 物件和陣列那些引用型別的資料而言的,由於這些存在淺拷貝的問題(不明白淺拷貝的看這篇文章),所以可以利用這點來實現子父元件的「同生共死」,看程式碼吧

// 假設 name 是一個物件或者陣列
props:['name'],
return {
  userName:this.name,
};

就這樣?!

沒錯就是這麼簡單粗暴,由於淺拷貝的問題,我們在子元件修改 userName 的時候,從父元件傳遞過來的那個值也會改變的,然後就會實現 props 的「雙向繫結」了。但是一般沒人會這麼幹,因為這樣會造成維護上的問題,會讓人覺得咋沒幹啥父元件的值咋就變了,會讓人頭禿啊,所以除非你專案中非得要搞這麼一個子父元件 props 的「同生共死」,那就這麼幹吧。

3 子元件呼叫父元件的方法

不知道這樣的叫法對不對,反正就這樣啦。總結之後又下面這幾種方法

3.1 emit

其實本來 emit 就是用於子元件向父元件通訊的,上面的子元件傳值給父元件其實也就是父元件監聽子元件的事件,然後觸發父元件的方法的,換個說法,也就是子元件呼叫了父元件的方法,再寫一下程式碼吧:

子元件

// methods 裡
update(){
  this.$emit('update','ljy');
  // 第一個引數:事件名,第二個引數:傳遞給事件方法的引數
}

父元件


/* template 裡面的程式碼,監聽子元件裡面的 update 事件,呼叫父元件的 childUpdate 方法

 <child :name="userName" @update="childUpdate"></child>

*/
// methods
chidlUpdate(val){
  // val 引數就是子元件傳遞過來的資料
  this.userName = val;
}

上面的程式碼中,從某種意義上來說,就是子元件呼叫了父元件的 childUpdate 方法。

3.2 this.$paarent.event

這個就比較簡單了,我們假設我們在父元件定義了一個 fatherMethod() 方法,然後我們子元件就可以通過下面的程式碼實現呼叫 fatherMethod() 的方法

childClick(){
  this.$parent.fatherMethod();
}

3.3 props

props 能傳遞 Function 型別的資料,所以,我們通過 props 當然也是可以直接的呼叫父元件傳遞過來的方法啦。不多說,直接擼程式碼:
父元件

<!-- 假設父元件裡定義了一個 fatherMethod() 方法 -->
<child :fatherMethod="fatherMethod"></child>

子元件

props: {
  fatherMethod: {
    type: Function,
    default: null
  }
},
methods: {
  childClick() {
    this.fatherMethod();
  }
},

這樣我們也是呼叫了父元件的方法啦。

4 父元件呼叫子元件的方法

這個,暫時沒有遇到過這種情況,不過以備不時之需,也寫一下吧。父元件呼叫子元件的方法的話是利用 ref 獲取到子元件例項,從而呼叫子元件的方法,假設我們子元件有這麼一個 childMethod() 方法。那麼我們的父元件就可以這麼來呼叫子元件的方法了

/* <child ref="con"></child> 子元件 */

methods: {
  update() {
    this.$refs.con.childMethod();
  },
}

至此,關於父子元件通訊的的話題就聊到這邊了,如果有啥錯誤或者遺漏的,歡迎在下面斧正啦。