原創不易,但願能關注下咱們,再順手點個贊~~ |
本文首發於政採雲前端團隊博客: Vue 組件數據通訊方案總結html
初識 Vue.js ,瞭解到組件是 Vue 的主要構成部分,但組件內部的做用域是相對獨立的部分,組件之間的關係通常以下圖:前端
組件 A 與組件 B 、C 之間是父子組件,組件 B 、C 之間是兄弟組件,而組件 A 、D 之間是隔代的關係。vue
那麼對於這些不一樣的關係,本文主要分享了他們之間能夠採用的幾種數據通訊方式,例如 Props 、$emit / $on 、Vuex 等,你們能夠根據本身的使用場景能夠選擇適合的使用方式。vuex
一、Prop 是你能夠在組件上註冊的一些自定義特性。當一個值傳遞給一個 Prop 特性的時候,它就變成了那個組件實例的一個屬性。父組件向子組件傳值,經過綁定屬性來向子組件傳入數據,子組件經過 Props 屬性獲取對應數據api
// 父組件
<template>
<div class="container">
<child :title="title"></child>
</div>
</template>
<script>
import Child from "./component/child.vue";
export default {
name: "demo",
data: function() {
return {
title: "我是父組件給的"
};
},
components: {
Child
},
};
</script>
複製代碼
// 子組件
<template>
<div class="text">{{title}}</div>
</template>
<script>
export default {
name: 'demo',
data: function() {},
props: {
title: {
type: String
}
},
};
</script>
複製代碼
二、$emit 子組件向父組件傳值(經過事件形式),子組件經過 $emit 事件向父組件發送消息,將本身的數據傳遞給父組件。數組
// 父組件
<template>
<div class="container">
<div class="title">{{title}}</div>
<child @changeTitle="parentTitle"></child>
</div>
</template>
<script>
import Child from "./component/child.vue";
export default {
name: "demo",
data: function() {
return {
title: null
};
},
components: {
Child
},
methods: {
parentTitle(e) {
this.title = e;
}
}
};
</script>
複製代碼
// 子組件
<template>
<div class="center">
<button @click="childTitle">我給父組件賦值</button>
</div>
</template>
<script>
export default {
name: 'demo',
data() {
return {
key: 1
};
},
methods: {
childTitle() {
this.$emit('changeTitle', `我給父組件的第${this.key}次`);
this.key++;
}
}
};
</script>
複製代碼
小總結:經常使用的數據傳輸方式,父子間傳遞。緩存
這個方法是經過建立了一個空的 vue 實例,當作 $emit 事件的處理中心(事件總線),經過他來觸發以及監聽事件,方便的實現了任意組件間的通訊,包含父子,兄弟,隔代組件。性能優化
// 父組件
<template>
<div class="container">
<child1 :Event="Event"></child1>
<child2 :Event="Event"></child2>
<child3 :Event="Event"></child3>
</div>
</template>
<script>
import Vue from "vue";
import Child1 from "./component/child1.vue";
import Child2 from "./component/child2.vue";
import Child3 from "./component/child3.vue";
const Event = new Vue();
export default {
name: "demo",
data: function() {
return {
Event: Event
};
},
components: {
Child1,
Child2,
Child3
},
};
</script>
複製代碼
// 子組件1
<template>
<div class="center">
1.個人名字是:{{name}}
<button @click="send">我給3組件賦值</button>
</div>
</template>
<script>
export default {
name: "demo1",
data() {
return {
name: "政採雲"
};
},
props: {
Event: Object
},
methods: {
send() {
this.Event.$emit("message-a", this.name);
}
}
};
</script>
複製代碼
// 子組件2
<template>
<div class="center">
2.個人年齡是:{{age}}歲
<button @click="send">我給3組件賦值</button>
</div>
</template>
<script>
/* eslint-disable */
export default {
name: "demo2",
data() {
return {
age: "3"
};
},
props: {
Event: Object
},
methods: {
send() {
this.Event.$emit("message-b", this.age);
}
}
};
</script>
複製代碼
// 子組件3
<template>
<div class="center">個人名字是{{name}},今年{{age}}歲</div>
</template>
<script>
export default {
name: 'demo3',
data() {
return {
name: '',
age: ''
};
},
props: {
Event: Object
},
mounted() {
this.Event.$on('message-a', name => {
this.name = name;
});
this.Event.$on('message-b', age => {
this.age = age;
});
},
};
</script>
複製代碼
小總結:巧妙的在父子,兄弟,隔代組件中均可以互相數據通訊。bash
Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。app
Vuex實現了一個單項數據流,經過建立一個全局的 State 數據,組件想要修改 State 數據只能經過 Mutation 來進行,例如頁面上的操做想要修改 State 數據時,須要經過 Dispatch (觸發 Action ),而 Action 也不能直接操做數據,還須要經過 Mutation 來修改 State 中數據,最後根據 State 中數據的變化,來渲染頁面。
// index.js
import Vue from 'vue';
import Tpl from './index.vue';
import store from './store';
new Vue({
store,
render: h => h(Tpl),
}).$mount('#app');
複製代碼
// store
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment(state) {
state.count++;
},
reduce(state) {
state.count--;
}
},
actions: {
actIncrement({ commit }) {
commit('increment');
},
actReduce({ commit }) {
commit('reduce');
}
},
getters: {
doubleCount: state => state.count*2
}
});
export default store;
複製代碼
// vue文件
<template>
<div class="container">
<p>個人count:{{count}}</p>
<p>doubleCount:{{doubleCount}}</p>
<button @click="this.actIncrement">增長</button>
<button @click="this.actReduce">減小</button>
</div>
</template>
<script>
import { mapGetters, mapActions, mapState } from "vuex";
export default {
name: "demo",
data: function() {
return {};
},
components: {},
props: {},
computed: {
...mapState(["count"]),
...mapGetters(["doubleCount"])
},
methods: {
...mapActions(["actIncrement", "actReduce"])
}
};
</script>
複製代碼
Vuex 中須要注意的點:
Mutation :是修改State數據的惟一推薦方法,且只能進行同步操做。
Getter :Vuex 容許在Store中定義 "Getter"(相似於 Store 的計算屬性)。Getter 的返回值會根據他的依賴進行緩存,只有依賴值發生了變化,纔會從新計算。
本段只是簡單介紹了一下 Vuex 的運行方式,更多功能例如 Module 模塊請參考官網 。
小總結:統一的維護了一份共同的 State 數據,方便組件間共同調用。
Vue 組件間傳輸數據在 Vue2.4 版本後有了新方法。除了 Props 外,還有了 $attrs / $listeners。
Class
和 Style
除外)。當一個組件沒有聲明任何 Prop 時,這裏會包含全部父做用域的綁定 (Class
和 Style
除外),而且能夠經過 v-bind="$attrs"
傳入內部組件——在建立高級別的組件時很是有用。下面來看個例子
// 父組件
<template>
<div class="container">
<button style="backgroundColor:lightgray" @click="reduce">減dd</button>
<child1 :aa="aa" :bb="bb" :cc="cc" :dd="dd" @reduce="reduce"></child1>
</div>
</template>
<script>
import Child1 from './component/child1.vue';
export default {
name: 'demo',
data: function() {
return {
aa: 1,
bb: 2,
cc: 3,
dd: 100
};
},
components: {
Child1
},
methods: {
reduce() {
this.dd--;
}
}
};
</script>
複製代碼
// 子組件1
<template>
<div>
<div class="center">
<p>aa:{{aa}}</p>
<p>child1的$attrs:{{$attrs}}</p>
<button @click="this.reduce1">組件1減dd</button>
</div>
<child2 v-bind="$attrs" v-on="$listeners"></child2>
</div>
</template>
<script>
import child2 from './child2.vue';
export default {
name: 'demo1',
data() {
return {};
},
props: {
aa: Number
},
components: {
child2
},
methods: {
reduce1() {
this.$emit('reduce');
}
}
};
</script>
複製代碼
// 子組件2
<template>
<div>
<div class="center">
<p>bb:{{bb}}</p>
<p>child2的$attrs:{{$attrs}}</p>
<button @click="this.reduce2">組件2減dd</button>
</div>
<child3 v-bind="$attrs"></child3>
</div>
</template>
<script>
import child3 from './child3.vue';
export default {
name: 'demo1',
data() {
return {};
},
props: {
bb: Number
},
components: {
child3
},
methods: {
reduce2() {
this.$emit('reduce');
}
}
};
</script>
複製代碼
// 子組件3
<template>
<div class="center">
<p>child3的$attrs:{{$attrs}}</p>
</div>
</template>
<script>
export default {
name: 'demo3',
data() {
return {};
},
props: {
dd: String
},
};
</script>
複製代碼
簡單來講,$attrs 裏存放的是父組件中綁定的非 props 屬性,$listeners 裏面存放的是父組件中綁定的非原生事件。
小總結:當傳輸數據、方法較多時,無需一一填寫的小技巧。
Vue2.2 版本之後新增了這兩個 API, 這對選項須要一塊兒使用,以容許一個祖先組件向其全部子孫後代注入一個依賴,不論組件層次有多深,並在其上下游關係成立的時間裏始終生效。 簡單來講,就是父組件經過 Provider 傳入變量,任意子孫組件經過 Inject 來拿到變量。
// 父組件
<template>
<div class="container">
<button @click="this.changeName">我要更名字了</button>
<p>個人名字:{{name}}</p>
<child1></child1>
</div>
</template>
<script>
import Child1 from './component/child1.vue';
export default {
name: 'demo',
data: function() {
return {
name: '政採雲'
};
},
// provide() {
// return {
// name: this.name //這種綁定方式是不可響應的
// };
// },
provide() {
return {
obj: this
};
},
components: {
Child1
},
methods: {
changeName() {
this.name = '政採雲前端';
}
}
};
</script>
複製代碼
// 子組件
<template>
<div>
<div class="center">
<!-- <p>子組件名字:{{name}}</p> -->
<p>子組件名字:{{this.obj.name}}</p>
</div>
<child2></child2>
</div>
</template>
<script>
import child2 from './child2.vue';
export default {
name: 'demo1',
data() {
return {};
},
props: {},
// inject: ["name"],
inject: {
obj: {
default: () => {
return {};
}
}
},
components: {
child2
},
};
</script>
複製代碼
須要注意的是:Provide 和 Inject 綁定並非可響應的。這是刻意爲之的。然而,若是你傳入了一個可監聽的對象,那麼其對象的屬性仍是可響應的。
因此,若是採用的是我代碼中註釋的方式,父級的 name 若是改變了,子組件this.name 是不會改變的,仍然是 政採雲,而當採用代碼中傳入一個監聽對象,修改對象中屬性值,是能夠監聽到修改的。
Provider / Inject 在項目中須要有較多公共傳參時使用仍是頗爲方便的。
小總結:傳輸數據父級一次注入,子孫組件一塊兒共享的方式。
this.$parent
訪問父實例,子實例被推入父實例的 $children
數組中。ref
特性 的全部 DOM 元素和組件實例。ref 被用來給元素或子組件註冊引用信息。引用信息將會註冊在父組件的 $refs
對象上。若是在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;若是用在子組件上,引用就指向組件。// 父組件
<template>
<div class="container">
<p>個人title:{{title}}</p>
<p>個人name:{{name}}</p>
<child1 ref="comp1"></child1>
<child2 ref="comp2"></child2>
</div>
</template>
<script>
import Child1 from './component/child1.vue';
import Child2 from './component/child2.vue';
export default {
name: 'demo',
data: function() {
return {
title: null,
name: null,
content: '就是我'
};
},
components: {
Child1,
Child2
},
mounted() {
const comp1 = this.$refs.comp1;
this.title = comp1.title;
comp1.sayHello();
this.name = this.$children[1].title;
},
};
</script>
複製代碼
// 子組件1-ref方式
<template>
<div>
<div class="center">個人父組件是誰:{{content}}</div>
</div>
</template>
<script>
export default {
name: 'demo1',
data() {
return {
title: '我是子組件',
content: null
};
},
mounted() {
this.content = this.$parent.content;
},
methods: {
sayHello() {
window.alert('Hello');
}
}
};
</script>
複製代碼
// 子組件2-children方式
<template>
<div>
<div class="center"></div>
</div>
</template>
<script>
export default {
name: 'demo1',
data() {
return {
title: '我是子組件2'
};
},
};
</script>
複製代碼
經過例子能夠看到這兩種方式均可以父子間通訊,而缺點也很統一,就是都不能跨級以及兄弟間通訊。
小總結:父子組件間共享數據以及方法的便捷實踐之一。
組件間不一樣的使用場景能夠分爲 3 類,對應的通訊方式以下:
你們能夠根據本身的使用場景選擇不一樣的通訊方式,固然仍是都本身寫寫代碼,試驗一把來的印象深入嘍。
招人,前端,隸屬政採雲前端大團隊(ZooTeam),50 餘個小夥伴正等你加入一塊兒浪 [壞笑] 若是你想改變一直被事折騰,但願開始能折騰事;若是你想改變一直被告誡須要多些想法,卻無從破局;若是你想改變你有能力去作成那個結果,卻不須要你;若是你想改變你想作成的事須要一個團隊去支撐,但沒你帶人的位置;若是你想改變既定的節奏,將會是「5年工做時間3年工做經驗」;若是你想改變原本悟性不錯,但老是有那一層窗戶紙的模糊… 若是你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的本身。若是你但願參與到隨着業務騰飛的過程,親手參與一個有着深刻的業務理解、完善的技術體系、技術創造價值、影響力外溢的前端團隊的成長曆程,我以爲咱們該聊聊。任什麼時候間,等着你寫點什麼,發給 ZooTeam@cai-inc.com