深入父子組件傳值

前言

之前學習vue,對父子組件傳值達成了基本的認識,現在我把它進行系統的整理

父組件向子組件傳值prop

首先在父組件中

<son :msg="我是你爸爸"></son>

在子組件的props中

export default {
    props:{
		'msg':String
	}
}

這裏我還加了一個數據類型的檢測,注意,數據類型是在組件實例創建之前進行驗證的

子組件向父組件傳值$emit

子組件中,需要觸發一個事件,可以是自定義事件,也可以是原生事件,在這裏我們先用自定義事件,原生事件比較複雜,我放在後面說

首先我們在子組件中用$emit觸發自定義事件,這個自定義事件寫在生命週期函數或者是click事件上均可,然後第二個參數是我們要傳給父組件的值

this.$emit('hellodad','爸爸我很想你');

然後我們在父組件中監聽子組件的自定義事件,並在監聽到的時候,觸發happy事件

<son v-on:hellodad="happy"></son>
export default {
    methods:{
      happy(data){
        console.log(data);
        console.log('hello,my son');
      }
    },
    components:{
      son
    }
}

data中就是子組件中打印出來的消息啦。
在這裏插入圖片描述

vm.$attrs

那麼什麼是$attrs呢,下面是官方定義

包含了父作用域中不作爲 prop 被識別 (且獲取) 的特性綁定 (class 和 style 除外)。當一個組件沒有聲明任何 prop 時,這裏會包含所有父作用域的綁定 (class 和 style 除外),並且可以通過 v-bind="$attrs" 傳入內部組件——在創建高級別的組件時非常有用。

父親給兒子傳了三個屬性

<son
	msg1:'1'
	msg2:'2'
	msg3:'3'
></son>

兒子說,我不要那麼多,要一個就好了,剩下給你孫子吧

<grandson v-bind="$attrs"></grandson>
export default {
	props:['msg1']
}

孫子說,氣死了,你怎麼盡挑不要的給我

created(){
	console.log(this.$attrs);//{v-bind:{msg2:'2',msg3:'3'}}
}

inheritAttrs

官方解釋

1.默認情況下父作用域的不被認作 props 的特性綁定 (attribute bindings) 將會“回退”且作爲普通的 HTML 特性應用在子組件的根元素上。當撰寫包裹一個目標元素或另一個組件的組件時,這可能不會總是符合預期行爲。通過設置 inheritAttrs 到 false,這些默認行爲將會被去掉。2.而通過 (同樣是 2.4 新增的) 實例屬性 $attrs 可以讓這些特性生效,且可以通過 v-bind 顯性的綁定到非根元素上
注意:這個選項不影響 class 和 style 綁定。

首先,我們來理解一下第一句話

在父元素中設置

<son msg="son" class="son"></son>

在子元素中不設置props進行接收

export default {
	inheritAttrs:false
}

在這裏插入圖片描述
父組件給子組件設置的值就會退化成爲子組件的屬性

在子組件中設置

exportt default{
	inheritAttrs:false
}

在這裏插入圖片描述
看,msg屬性消失了,它不會退化爲子組件普通的 HTML 特性了,但是classstyle則不會。這是因爲,在父組件中不被認作 props 的特性綁定會覆蓋子組件根組件上的同名特性,而classstyle則會發生合併。

2.然後,我們來理解一下第二句話,因爲有$attrs,我們用祖孫三輩組件來進行解釋

父親

<son
	msg1:'1'
	msg2:'2'
	msg3:'3'
></son>

兒子

<grandson v-bind="$attrs"></grandson>
export default {
	props:['msg1']
}

可以看到,這些特性生效,且可以通過 v-bind 顯性的綁定到元素上。至於是不是根元素,其實主要是看你的寫法,如果你是直接在子組件的components中定義孫子元素,並且把$attrs綁定在孫子上面,那理解爲非根元素也無可厚非。
在這裏插入圖片描述

.native

使用 v-on 的.native 修飾符,可以在直接監聽組件的根元素的一個原生事件。

<base-input v-on:focus.native="onFocus"></base-input>

我在給router-link綁定點擊事件的時候,就需要使用.native修飾符,否則點擊事件不會生效。因爲router-link是一個自定義標籤,他沒有自己的事件和方法,所以我們用.native監聽這個組件根元素的原生事件,就可以觸發點擊事件了。
可是有的時候,在你嘗試監聽一個類似 的非常特定的元素時,這並不是個好主意。比如上述 組件可能做了如下重構,所以根元素實際上是一個

<label>
  {{ label }}
  <input
    v-bind="$attrs"
    v-bind:value="value"
    v-on:input="$emit('input', $event.target.value)"
  >
</label>

這時,父級的 .native 監聽器將靜默失敗。它不會產生任何報錯,但是 onFocus 處理函數不會如你預期地被調用。

$listeners

爲了解決這個問題,Vue 提供了一個 $listeners 屬性,它是一個對象,裏面包含了作用在這個組件上的所有監聽器。例如:

{
  focus: function (event) { /* ... */ }
  input: function (value) { /* ... */ },
}

下面是$listeners的官方解釋

包含了父作用域中的 (不含 .native 修飾器的) v-on 事件監聽器。它可以通過 v-on="$listeners" 傳入內部組件——在創建更高層次的組件時非常有用。

啥意思呢?我們還是用祖孫三輩舉例
父親和兒子說,請把孫子今天的表現告訴我

<son @actiom="happy"></son>

兒子:幫忙父親監視孫子

<grandson v-on="$listeners"><grandson>

孫子

this.$emit('action',msg);

有的時候,我們還可以用computed覆寫$listeners這個事件監聽器

computed:{
	mylisteners:function(){
		var _this = this;
		return Object.assign({},this.$listens,{
			input:function()(event) {
            _this.$emit('input', event.target.value)
          }
		}
	}
}

.sync

在有些情況下,我們可能需要對一個 prop 進行“雙向綁定”。不幸的是,真正的雙向綁定會帶來維護上的問題,因爲子組件可以修改父組件,且在父組件和子組件都沒有明顯的改動來源。

這也是爲什麼我們推薦以 update:myPropName 的模式觸發事件取而代之。舉個例子,在一個包含 title prop 的假設的組件中,我們可以用以下方法表達對其賦新值的意圖:

this.$emit('update:title', newTitle)

然後父組件可以監聽那個事件並根據需要更新一個本地的數據屬性。例如:

<text-document
  v-bind:title="doc.title"
  v-on:update:title="doc.title = $event"
></text-document>

爲了方便起見,我們爲這種模式提供一個縮寫,即 .sync 修飾符:

<text-document v-bind:title.sync="doc.title"></text-document>

所以.sync的作用就是

<text-document v-on:update:tiltle="doc.title=$event"></text-document>

注意帶有 .sync 修飾符的 v-bind 不能和表達式一起使用 (例如 v-bind:title.sync=”doc.title + ‘!’” 是無效的)。取而代之的是,你只能提供你想要綁定的屬性名,類似 v-model。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章