那些年,Vue資料傳遞的各種方式

學習Blog
10 min readJan 3, 2021

--

前端框架Vue

此文章內容使用的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"

線上DEMO點我

父層傳資料給子層 (多層傳遞)

三層傳遞

前面提到的都是父層直接傳給子層,如果遇到需要傳遞兩次的情況怎麼辦?

雖然子層也是可以經過父層再取得祖父層,祖父層也可以經過父層再取得子層,但實在是不怎麼方便,這時候可以使用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的方法可以用於三層的組件結構,但遇到超過三層的組件結構時,情況變得很複雜

狀況1 子組件在深處

雖然v-slot可以像是俄羅斯娃娃那樣不停的使用,但想必不太會有人想要這樣做?

    <component1>
<component3></component3>
</component1>
component1: <div>
<component2>
<slot></slot>
</component2>
</div>
component2: <div>
<slot></slot>
</div>
俄羅斯娃娃 (https://pxhere.com/zh/photo/493154)

接著還有一個情況,如果是同一層的組件之間要傳遞資料的話,則需要經過父層組件,一樣也是相當不方便:

狀況2 同層組件

還記得前面所提到的$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] EventBus 事件總線

[Vue] Event Bus 是什麼? 怎麼用?

在小型的專案或許可以這樣做,但在具有比較規模的專案,Vue官網推薦使用Vuex來管理專案

關於Vuex的使用很推薦看這篇文章,寫得相當詳細:

[Vue] Vuex 是什麼? 怎麼用? — State、Mutations (1/5)

附註一點,當使用Vuex久了,有很多資料不一定會使用到,這時候可以考慮動態註冊Vuex內的model達到節省資源的效果(網址點我)。

--

--

學習Blog
學習Blog

No responses yet