背景:前端
初始Vue.js,瞭解組件時Vue的主要構成部分,但組件內部的做用域是相對獨立的部分,組件之間的關係通常以下圖:vue
組件A與組件B、C之間是父子組件,組件B、C之間是兄弟組件,而組件A、D之間是隔代的關係web
那麼對於這些不一樣的關係,本文主要分享了他們之間能夠採用的幾種數據通訊方式。例如Props、$emit/$on、Vuex等。能夠根據使用場景選擇適合的使用方式。vuex
一、Prop是你能夠在組件上註冊的一些自定義特性。當一個值傳遞給一個Prop特性的時候,它就變成了那個組件實例的一個屬性。父組件向子組件傳值,經過綁定屬性來向子組件傳入數據,子組件經過Props屬性獲取對應數據數組
//父組件 <template> <div> <child :title="title"></child> </div> </template> <script> import Child from './child.vue' export defalut{ name:'parent', data(){ return{ title:'我是父組件給的' } }, components:{ Child } } </script> //子組件 <template> <div> <p>{{title}}</p> </div> </template> <script> export default{ name:'child', data(){ return{ } }, props:['title'] } </script>
二、$emit 子組件向父組件傳值(經過事件形式),子組件經過 $emit 事件向父組件發送消息,將本身的數據傳遞給父組件。緩存
1 // 父組件 2 <template> 3 <div class="container"> 4 <div class="title">{{title}}</div> 5 <child @changeTitle="parentTitle"></child> 6 </div> 7 </template> 8 9 <script> 10 import Child from "./component/child.vue"; 11 12 export default { 13 name: "demo", 14 data: function() { 15 return { 16 title: null 17 }; 18 }, 19 components: { 20 Child 21 }, 22 methods: { 23 parentTitle(e) { 24 this.title = e; 25 } 26 } 27 }; 28 </script> 29 30 31 // 子組件 32 <template> 33 <div class="center"> 34 <button @click="childTitle">我給父組件賦值</button> 35 </div> 36 </template> 37 38 <script> 39 export default { 40 name: 'demo', 41 data() { 42 return { 43 key: 1 44 }; 45 }, 46 methods: { 47 childTitle() { 48 this.$emit('changeTitle', `我給父組件的第${this.key}次`); 49 this.key++; 50 } 51 } 52 }; 53 </script>
小總結:經常使用的數據傳輸方式,父子間傳遞。app
這個方法是經過建立了一個空的 vue 實例,當作 $emit 事件的處理中心(事件總線),經過他來觸發以及監聽事件,方便的實現了任意組件間的通訊,包含父子,兄弟,隔代組件。ide
1 // 父組件 2 <template> 3 <div class="container"> 4 <child1 :Event="Event"></child1> 5 <child2 :Event="Event"></child2> 6 <child3 :Event="Event"></child3> 7 </div> 8 </template> 9 10 <script> 11 import Vue from "vue"; 12 13 import Child1 from "./component/child1.vue"; 14 import Child2 from "./component/child2.vue"; 15 import Child3 from "./component/child3.vue"; 16 17 const Event = new Vue(); 18 19 export default { 20 name: "demo", 21 data: function() { 22 return { 23 Event: Event 24 }; 25 }, 26 components: { 27 Child1, 28 Child2, 29 Child3 30 }, 31 }; 32 </script> 33 34 35 // 子組件1 36 <template> 37 <div class="center"> 38 1.個人名字是:{{name}} 39 <button @click="send">我給3組件賦值</button> 40 </div> 41 </template> 42 43 <script> 44 export default{ 45 name: "demo1", 46 data() { 47 return { 48 name: "政採雲" 49 }; 50 }, 51 props: { 52 Event 53 }, 54 methods: { 55 send() { 56 this.Event.$emit("message-a", this.name); 57 } 58 } 59 }; 60 </script> 61 62 63 // 子組件2 64 <template> 65 <div class="center"> 66 2.個人年齡是:{{age}}歲 67 <button @click="send">我給3組件賦值</button> 68 </div> 69 </template> 70 71 <script> 72 /* eslint-disable */ 73 export default { 74 name: "demo2", 75 data() { 76 return { 77 age: "3" 78 }; 79 }, 80 props: { 81 Event 82 }, 83 methods: { 84 send() { 85 this.Event.$emit("message-b", this.age); 86 } 87 } 88 }; 89 </script> 90 91 92 // 子組件3 93 <template> 94 <div class="center">個人名字是{{name}},今年{{age}}歲</div> 95 </template> 96 97 <script> 98 export default{ 99 name: 'demo3', 100 data() { 101 return { 102 name: '', 103 age: '' 104 }; 105 }, 106 props: { 107 Event 108 }, 109 mounted() { 110 this.Event.$on('message-a', name => { 111 this.name = name; 112 }); 113 this.Event.$on('message-b', age => { 114 this.age = age; 115 }); 116 }, 117 }; 118 </script>
小總結:巧妙的在父子,兄弟,隔代組件中均可以互相數據通訊。ui
Vuex[1] 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。this
Vuex 實現了一個單項數據流,經過建立一個全局的 State 數據,組件想要修改 State 數據只能經過 Mutation 來進行,例如頁面上的操做想要修改 State 數據時,須要經過 Dispatch (觸發 Action ),而 Action 也不能直接操做數據,還須要經過 Mutation 來修改 State 中數據,最後根據 State 中數據的變化,來渲染頁面。
1 // index.js 2 import Vue from 'vue'; 3 import Tpl from './index.vue'; 4 import store from './store'; 5 6 new Vue({ 7 store, 8 render: h => h(Tpl), 9 }).$mount('#app'); 10 11 12 // store 13 import Vue from 'vue'; 14 import Vuex from 'vuex'; 15 16 Vue.use(Vuex); 17 18 const store = new Vuex.Store({ 19 state: { 20 count: 1 21 }, 22 mutations: { 23 increment(state) { 24 state.count++; 25 }, 26 reduce(state) { 27 state.count--; 28 } 29 }, 30 actions: { 31 actIncrement({ commit }) { 32 commit('increment'); 33 }, 34 actReduce({ commit }) { 35 commit('reduce'); 36 } 37 }, 38 getters: { 39 doubleCount: state => state.count*2 40 } 41 }); 42 43 export default store; 44 45 46 // vue文件 47 <template> 48 <div class="container"> 49 <p>個人count:{{count}}</p> 50 <p>doubleCount:{{doubleCount}}</p> 51 <button @click="this.actIncrement">增長</button> 52 <button @click="this.actReduce">減小</button> 53 </div> 54 </template> 55 56 <script> 57 import { mapGetters, mapActions, mapState } from "vuex"; 58 59 export default { 60 name: "demo", 61 data: function() { 62 return {}; 63 }, 64 components: {}, 65 props: {}, 66 computed: { 67 ...mapState(["count"]), 68 ...mapGetters(["doubleCount"]) 69 }, 70 methods: { 71 ...mapActions(["actIncrement", "actReduce"]) 72 } 73 }; 74 </script>
Vuex 中須要注意的點:
Mutation :是修改State數據的惟一推薦方法,且只能進行同步操做。
Getter :Vuex 容許在Store中定義 「 Getter」(相似於 Store 的計算屬性)。Getter 的返回值會根據他的依賴進行緩存,只有依賴值發生了變化,纔會從新計算。
本段只是簡單介紹了一下 Vuex 的運行方式,更多功能例如 Module 模塊請參考官網[2] 。
小總結:統一的維護了一份共同的 State 數據,方便組件間共同調用。
Vue 組件間傳輸數據在 Vue 2.4 版本後有了新方法。除了 Props 外,還有了 $attrs / $listeners。
• $attrs: 包含了父做用域中不做爲 Prop 被識別 (且獲取) 的特性綁定(Class
和 Style
除外)。當一個組件沒有聲明任何 Prop 時,這裏會包含全部父做用域的綁定 (Class
和 Style
除外),而且能夠經過 v-bind="$attrs"
傳入內部組件——在建立高級別的組件時很是有用。
• $listeners: 包含了父做用域中的 (不含 .native 修飾器的) v-on 事件監聽器。它能夠經過 v-on="$listeners" 傳入內部組件
下面來看個例子
1 // 父組件 2 <template> 3 <div class="container"> 4 <button style="backgroundColor:lightgray" @click="reduce">減dd</button> 5 <child1 :aa="aa" :bb="bb" :cc="cc" :dd="dd" @reduce="reduce"></child1> 6 </div> 7 </template> 8 9 <script> 10 import Child1 from './component/child1.vue'; 11 export default { 12 name: 'demo', 13 data: function() { 14 return { 15 aa: 1, 16 bb: 2, 17 cc: 3, 18 dd: 100 19 }; 20 }, 21 components: { 22 Child1 23 }, 24 methods: { 25 reduce() { 26 this.dd--; 27 } 28 } 29 }; 30 </script> 31 32 33 // 子組件1 34 <template> 35 <div> 36 <div class="center"> 37 <p>aa:{{aa}}</p> 38 <p>child1的$attrs:{{$attrs}}</p> 39 <button @click="this.reduce1">組件1減dd</button> 40 </div> 41 <child2 v-bind="$attrs" v-on="$listeners"></child2> 42 </div> 43 </template> 44 45 <script> 46 import child2 from './child2.vue'; 47 export default { 48 name: 'demo1', 49 data() { 50 return {}; 51 }, 52 props: { 53 aa: Number 54 }, 55 components: { 56 child2 57 }, 58 methods: { 59 reduce1() { 60 this.$emit('reduce'); 61 } 62 } 63 }; 64 </script> 65 66 67 // 子組件2 68 <template> 69 <div> 70 <div class="center"> 71 <p>bb:{{bb}}</p> 72 <p>child2的$attrs:{{$attrs}}</p> 73 <button @click="this.reduce2">組件2減dd</button> 74 </div> 75 <child3 v-bind="$attrs"></child3> 76 </div> 77 </template> 78 79 <script> 80 import child3 from './child3.vue'; 81 export default { 82 name: 'demo1', 83 data() { 84 return {}; 85 }, 86 props: { 87 bb: Number 88 }, 89 components: { 90 child3 91 }, 92 methods: { 93 reduce2() { 94 this.$emit('reduce'); 95 } 96 } 97 }; 98 </script> 99 100 101 // 子組件3 102 <template> 103 <div class="center"> 104 <p>child3的$attrs:{{$attrs}}</p> 105 </div> 106 </template> 107 108 <script> 109 export default { 110 name: 'demo3', 111 data() { 112 return {}; 113 }, 114 props: { 115 dd: String 116 }, 117 }; 118 </script>
簡單來講,$attrs 裏存放的是父組件中綁定的非 props 屬性,$listeners 裏面存放的是父組件中綁定的非原生事件。
小總結:當傳輸數據、方法較多時,無需一一填寫的小技巧。
Vue 2.2 版本之後新增了這兩個 API , 這對選項須要一塊兒使用,以容許一個祖先組件向其全部子孫後代注入一個依賴,不論組件層次有多深,並在其上下游關係成立的時間裏始終生效。 簡單來講,就是父組件經過 Provider 傳入變量,任意子孫組件經過 Inject 來拿到變量。
1 // 父組件 2 <template> 3 <div class="container"> 4 <button @click="this.changeName">我要更名字了</button> 5 <p>個人名字:{{name}}</p> 6 <child1></child1> 7 </div> 8 </template> 9 10 <script> 11 import Child1 from './component/child1.vue'; 12 export default { 13 name: 'demo', 14 data: function() { 15 return { 16 name: '政採雲' 17 }; 18 }, 19 // provide() { 20 // return { 21 // name: this.name //這種綁定方式是不可響應的 22 // }; 23 // }, 24 provide() { 25 return { 26 obj: this 27 }; 28 }, 29 components: { 30 Child1 31 }, 32 methods: { 33 changeName() { 34 this.name = '政採雲前端'; 35 } 36 } 37 }; 38 </script> 39 40 41 // 子組件 42 <template> 43 <div> 44 <div class="center"> 45 <!-- <p>子組件名字:{{name}}</p> --> 46 <p>子組件名字:{{this.obj.name}}</p> 47 </div> 48 <child2></child2> 49 </div> 50 </template> 51 52 <script> 53 import child2 from './child2.vue'; 54 55 export default { 56 name: 'demo1', 57 data() { 58 return {}; 59 }, 60 props: {}, 61 // inject: ["name"], 62 inject: { 63 obj: { 64 default: () => { 65 return {}; 66 } 67 } 68 }, 69 components: { 70 child2 71 }, 72 }; 73 </script>
須要注意的是: Provide 和 Inject 綁定並非可響應的。這是刻意爲之的。然而,若是你傳入了一個可監聽的對象,那麼其對象的屬性仍是可響應的。
因此,若是採用的是我代碼中註釋的方式,父級的 name 若是改變了,子組件this.name 是不會改變的,仍然是 政採雲 ,而當採用代碼中傳入一個監聽對象,修改對象中屬性值,是能夠監聽到修改的。
Provider / Inject 在項目中須要有較多公共傳參時使用仍是頗爲方便的。
小總結:傳輸數據父級一次注入,子孫組件一塊兒共享的方式。
• $parent / $children: 指定已建立的實例之父實例,在二者之間創建父子關係。子實例能夠用 this.$parent
訪問父實例,子實例被推入父實例的 $children
數組中。
• $refs: 一個對象,持有註冊過 ref
特性[3] 的全部 DOM 元素和組件實例。ref 被用來給元素或子組件註冊引用信息。引用信息將會註冊在父組件的 $refs
對象上。若是在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;若是用在子組件上,引用就指向組件。
1 // 父組件 2 <template> 3 <div class="container"> 4 <p>個人title:{{title}}</p> 5 <p>個人name:{{name}}</p> 6 <child1 ref="comp1"></child1> 7 <child2 ref="comp2"></child2> 8 </div> 9 </template> 10 11 <script> 12 import Child1 from './component/child1.vue'; 13 import Child2 from './component/child2.vue'; 14 export default { 15 name: 'demo', 16 data: function() { 17 return { 18 title: null, 19 name: null, 20 content: '就是我' 21 }; 22 }, 23 components: { 24 Child1, 25 Child2 26 }, 27 mounted() { 28 const comp1 = this.$refs.comp1; 29 this.title = comp1.title; 30 comp1.sayHello(); 31 this.name = this.$children[1].title; 32 }, 33 }; 34 </script> 35 36 37 // 子組件1-ref方式 38 <template> 39 <div> 40 <div class="center">個人父組件是誰:{{content}}</div> 41 </div> 42 </template> 43 44 <script> 45 export default { 46 name: 'demo1', 47 data() { 48 return { 49 title: '我是子組件', 50 content: null 51 }; 52 }, 53 mounted() { 54 this.content = this.$parent.content; 55 }, 56 methods: { 57 sayHello() { 58 window.alert('Hello'); 59 } 60 } 61 }; 62 </script> 63 64 65 // 子組件2-children方式 66 <template> 67 <div> 68 <div class="center"></div> 69 </div> 70 </template> 71 72 <script> 73 export default{ 74 name: 'demo1', 75 data() { 76 return { 77 title: '我是子組件2' 78 }; 79 }, 80 }; 81 </script>
經過例子能夠看到這兩種方式均可以父子間通訊,而缺點也很統一,就是都不能跨級以及兄弟間通訊。
小總結:父子組件間共享數據以及方法的便捷實踐之一。
組件間不一樣的使用場景能夠分爲 3 類,對應的通訊方式以下:
• 父子通訊:Props / $emit,$emit / $on,Vuex,$attrs / $listeners,provide/inject,$parent / $children&$refs
• 兄弟通訊:$emit / $on,Vuex
• 隔代(跨級)通訊:$emit / $on,Vuex,provide / inject,$attrs / $listeners