此文章內容使用的Vue版本是2.6.11,官網點我
目前Vue.js是最受歡迎的前端框架之一,而組件(component)之間的資料傳遞是Vue的基礎、重要的概念,這邊整理、紀錄目前看過的傳遞方法:
父層傳資料給子層 (基本寫法)
身為Vue初學者的筆者,第一個學到的資料傳遞方式,是父層組件用V-bind傳遞資料給子層組件的props,子層使用$emit發出事件,父層v-on監聽到事件後,使用父層裡面的method修改父層裡面的data,相信大多人也是先學到這個方法
其中子組件寫法:
用法:
<子組件名稱
v-bind:子組件props名稱="要傳給子組件的data名稱"
v-on:子組件emit事件名稱="method名稱"
></子組件名稱>例子:
<child1-component
v-bind:value-form-parent="parentValue"
v-on:updateChildValue="handleChildValue"
></child1-component>// 子層component中的methods:
this.$emit('updateChildValue', payload)
v-bind、v-on可以簡化為:與@
<child1-component
:value-form-parent="parentValue"
@updateChildValue="handleChildValue"
></child1-component>
其中v-on有個地方很特別,猜猜看下列寫法有什麼差別?
// 寫法1
@updateChildValue="handleChildValue"// 寫法2
@updateChildValue="handleChildValue()"
寫法1類似於JS中的addEventListener,寫法2則是執行函式handleChildValue ,兩個方法所得到的$event都不同,前者會得到addEventListener的event,後者則是會取得emit該event傳遞的資料,所以前面提到的寫法等同於下列:
<child1-component
:value-form-parent="parentValue"
@updateChildValue="handleChildValue($event)"
></child1-component>
當知道$event的意義後,我們可以再寫得更精簡(偷懶)一點,不要用組件的method(如果只要簡單賦值的話):
<child1-component
:value-form-parent="parentValue"
@updateChildValue="parentValue = $event"
></child1-component>
實際上Vue有個語法糖叫sync(連結),可以用來簡化上面這種寫法:
<child1-component
:value-form-parent.sync="parentValue"
></child1-component>//子層component中的methods要補上update:
this.$emit('update:valueFormParent', payload)
實際上v-model也是做類似的事情(連結):
所以如果只要傳一個值給子組件,也是可以用v-model的:
<child1-component
v-model="parentValue"
></child1-component>// 子層component中的props:
props:['value']// 子層component中的methods:
this.$emit('input', payload)
說到這邊,如果我們不需要讓子組件取得父組件的值,其實可以把父組件的method傳給子組件使用,但使用的值是子組件的,同樣達到傳遞資料的效果
<child5-component
:value-form-parent="handleChildValue"
></child5-component>//父層component中的method: handleChildValue(payload){
this.parentValue = payload
}//子層component中的props:
props:['valueFormParent']//子層component中的methods:
this.valueFormParent(payload) //payload來自子組件,但方法用的是父層
以上的方法如果不清楚,可以看線上DEMO(點我)
父層傳資料給子層 (直接操作組件資料)
除了上述方式外,Vue還有提供其他操作資料的方法(官方介紹網址):
$root、$parent、$children
// $root:取得最上層組件
// $parent:取得上層組件
// $children:取得下組件,用陣列表示root-component: new Vue({
el: "#app",
data: {
something: "default value"
}
});parent-component:Vue.component("parent-component", {
data() {
return {
parentData: 'cat'
}
},
methods:{
parentMethod(){
console.log(this.$root.something) // "default value"
console.log(this.$children[0].childData) // "dog"
}
}
})child-component:Vue.component("child-component", {
data() {
return {
childData: "dog"
}
},
methods:{
childMethod(){
console.log(this.$root.something) // "default value"
console.log(this.$parent.parentData) // "cat"
}
}
})
$refs、ref
不過使用this.$children[0]這種寫法會不知道自己取得的是哪個組件,這時候可以用ref指定名稱,用$refs取得組件
parent-component: template: <child-component ref="child1"></child-component>methods:
console.log(this.$refs.child1.childData) // "dog"
父層傳資料給子層 (多層傳遞)
三層傳遞
前面提到的都是父層直接傳給子層,如果遇到需要傳遞兩次的情況怎麼辦?
雖然子層也是可以經過父層再取得祖父層,祖父層也可以經過父層再取得子層,但實在是不怎麼方便,這時候可以使用v-slot的方式將子層提升到祖父層底下
grandparent-component: <div id="app">
<parent-component>
<child-component
:dataFromGrandParent="grandParentData" //傳祖父層資料
></child-component>
</parent-component>
</div>parent-component: <div class="parentComponent">
<slot></slot> // child-component被放入的位置
</div>
另外值得一提的是slot內也可以指定傳遞資料,但就傳遞方式來說,並不是很直觀,有興趣的人可以看這個DEMO
多層傳遞
v-slot的方法可以用於三層的組件結構,但遇到超過三層的組件結構時,情況變得很複雜
雖然v-slot可以像是俄羅斯娃娃那樣不停的使用,但想必不太會有人想要這樣做?
<component1>
<component3></component3>
</component1>
component1: <div>
<component2>
<slot></slot>
</component2>
</div>component2: <div>
<slot></slot>
</div>
接著還有一個情況,如果是同一層的組件之間要傳遞資料的話,則需要經過父層組件,一樣也是相當不方便:
還記得前面所提到的$root嗎? Vue有種叫$Bus的作法,在$root註冊一個每個組件都能取得的Vue實例(instance),用來解決這種距離比較遠、不好處理的資料傳遞:
const bus = new Vue();Vue.component("component1", {
methods:{
passValue(){
bus.$emit('EventName', payload)
}
}
})Vue.component("component2", {
created:{
bus.$on('EventName', doSomething)
}
},
methods:{
doSomething(payload){
...
}
}
})
bus使用$emit發出事件,並帶了參數payload,接著另一個組件監聽事件,取得參數payload (DEMO點我)
Bus詳細作法:
在小型的專案或許可以這樣做,但在具有比較規模的專案,Vue官網推薦使用Vuex來管理專案
關於Vuex的使用很推薦看這篇文章,寫得相當詳細:
[Vue] Vuex 是什麼? 怎麼用? — State、Mutations (1/5)
附註一點,當使用Vuex久了,有很多資料不一定會使用到,這時候可以考慮動態註冊Vuex內的model達到節省資源的效果(網址點我)。