本文為轉載,原文:Vue學習筆記入門篇——組件的通訊
組件意味著協同工作,通常父子組件會是這樣的關系:組件 A 在它的模版中使用了組件 B。它們之間必然需要相互通信:父組件要給子組件傳遞數據,子組件需要將它內部發生的事情告知給父組件。然而,在一個良好定義的接口中盡可能將父子組件解耦是很重要的。這保證了每個組件可以在相對隔離的環境中書寫和理解,也大幅提高了組件的可維護性和可重用性。
在 Vue 中,父子組件的關系可以總結為 props down, events up。父組件通過 props 向下傳遞數據給子組件,子組件通過 events 給父組件發送消息。看看它們是怎么工作的。
Prop
使用Prop傳遞數據
組件實例的作用域是孤立的。這意味著不能 (也不應該) 在子組件的模板內直接引用父組件的數據。要讓子組件使用父組件的數據,我們需要通過子組件的 props 選項。
子組件要顯式地用 props 選項聲明它期待獲得的數據:
<div id="example">
<input v-model="parentMsg"><br>
<child :my-message="parentMsg"></child>
</div>
Vue.component('child', {
props:['myMessage'],
template:'<span>{{myMessage}}</span>'
})
new Vue({
el:'#example',
data:{
parentMsg:''
}
})
注意:
HTML 特性是不區分大小寫的。所以,當使用的不是字符串模版,camelCased (駝峰式) 命名的 prop 需要轉換為相對應的 kebab-case (短橫線隔開式) 命名
結果如下:
這樣,每當父組件的數據變化時,該變化也會傳導給子組件。
字面量語法 vs 動態語法
初學者常犯的一個錯誤是使用字面量語法傳遞數值:
<!-- 傳遞了一個字符串 "1" -->
<comp some-prop="1"></comp>
因為它是一個字面 prop,它的值是字符串 "1" 而不是 number。如果想傳遞一個實際的 number,需要使用 v-bind,從而讓它的值被當作 JavaScript 表達式計算:
<!-- 傳遞實際的 number -->
<comp v-bind:some-prop="1"></comp>
單向數據流
prop 是單向綁定的:當父組件的屬性變化時,將傳導給子組件,但是不會反過來。這是為了防止子組件無意修改了父組件的狀態——這會讓應用的數據流難以理解。
另外,每次父組件更新時,子組件的所有 prop 都會更新為最新值。這意味著你不應該在子組件內部改變 prop。如果你這么做了,Vue 會在控制臺給出警告。
為什么我們會有修改 prop 中數據的沖動呢?通常是這兩種原因:
prop 作為初始值傳入后,子組件想把它當作局部數據來用;
prop 作為初始值傳入,由子組件處理成其它數據輸出。
對這兩種原因,正確的應對方式是:
- 定義一個局部變量,并用 prop 的值初始化它:
props: ['initialCounter'],
data: function () {
return { counter: this.initialCounter }
}
- 定義一個計算屬性,處理 prop 的值并返回:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
Prop驗證
我們可以為組件的 props 指定驗證規格。如果傳入的數據不符合規格,Vue 會發出警告。當組件給其他人使用時,這很有用。
要指定驗證規格,需要用對象的形式,而不能用字符串數組:
Vue.component('example', {
props: {
// 基礎類型檢測 (`null` 意思是任何類型都可以)
propA: Number,
// 多種類型
propB: [String, Number],
// 必傳且是字符串
propC: {
type: String,
required: true
},
// 數字,有默認值
propD: {
type: Number,
default: 100
},
// 數組/對象的默認值應當由一個工廠函數返回
propE: {
type: Object,
default: function () {
return { message: 'hello' }
}
},
// 自定義驗證函數
propF: {
validator: function (value) {
return value > 10
}
}
}
})
type 可以是下面原生構造器:
- String
- Number
- Boolean
- Function
- Object
- Array
- Symbol
type 也可以是一個自定義構造器函數,使用 instanceof 檢測。
當 prop 驗證失敗,Vue 會在拋出警告 (如果使用的是開發版本)。注意 props 會在組件示例創建之前進行校驗,所以在 default 或 validator 函數里,諸如 data、computed 或 methods 的示例屬性還無法使用。
自定義事件
我們知道,父組件是使用 props 傳遞數據給子組件,但如果子組件要把數據傳遞回去,應該怎樣做?那就是自定義事件!
使用v-on綁定自定義事件
每個 Vue 實例都實現了事件接口 (Events interface),即:
使用 $on(eventName) 監聽事件
使用 $emit(eventName) 觸發事件
請看下面的例子:
<div id="counter-event-example">
<p>{{total}}</p>
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
Vue.component('button-counter',{
template:'<button v-on:click="increment">{{counter}}</button>',
data:function () {
return {
counter : 0
}
},
methods:{
increment:function () {
this.counter += 1
this.$emit('increment')
}
}
})
new Vue({
el:'#counter-event-example',
data:{
total:0
},
methods:{
incrementTotal:function () {
this.total += 1
}
}
})
運行結果如下
在本例中,子組件已經和它外部完全解耦了。它所做的只是報告自己的內部事件,至于父組件是否關心則與它無關。留意到這一點很重要。
給組件綁定原生事件
有時候,你可能想在某個組件的根元素上監聽一個原生事件。可以使用 .native 修飾 v-on。例如:
<my-component v-on:click.native="doTheThing"></my-component>
雙線綁定的實現
<div id="app">
<switchbtn :result="result" @on-result-change="onResultChange"></switchbtn>
<input type="button" value="change" @click="change">
</div>
Vue.component('switchbtn',{
template:"<div @click='change'>{{myResult?'開':'關'}}</div>",
props:['result'],
data:function () {
return {
myResult:this.result
}
},
watch:{
result : function (val) {
this.myResult = val;
},
myResult:function (val) {
this.$emit("on-result-change", val);
}
},
methods:{
change:function () {
this.myResult = !this.myResult;
}
}
})
var app = new Vue({
el:'#app',
data:{
result:true
},
methods:{
change:function () {
this.result = !this.result;
},
onResultChange:function (val) {
this.result = val;
}
}
})
完
本文為原創,轉載請注明出處
上一節:Vue學習筆記入門篇——組件的使用
返回目錄
下一節:Vue學習筆記入門篇——組件的內容分發(slot)