本篇說一下組件通信的問題,父子組件通信,前面的博客中已有說明,vue也推薦props in,event out;兄弟節點通信如何做呢?官方其實也給出了實現方式,我們以下面的場景來實現一下:
上圖中,實現如下功能:搜索表單組件中,包含各種搜索條件,當點擊搜索按鈕時,加載數據到列表組件中渲染。
這里會給出三種實現方式,不涉及合適與否,只為演示。
1、使用父組件進行封裝,把所有操作都移到父組件中
2、搜索組件,觸發事件到父組件,父組件監聽到事件發生,則執行查詢操作,傳遞props 到列表組件,這也是我們前面實現過的方式,這里簡單寫一個demo。
首先定義我們的組件:SearchComponent 、AppComponent、ListComponent
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>demo4</title> <script src="https://cdn.bootcss.com/vue/2.4.1/vue.js"></script> </head> <body> <div id="app"> <app></app> </div> <script> var SearchComponent = { template:` <div class="toolbar"> <input type="text" placeholder="keyword" v-model="keyword"/> <input type="text" placeholder="description" v-model="desc"/> <input type="button" value="search" @click="search()" /> </div> `, data:function(){ return { keyword:'', desc:'' } }, methods:{ search:function(){ this.$emit('onsearch',{keyword:this.keyword,desc:this.desc}); } } } var ListComponent = { template:` <div class="list" > {{list}} </div> `, props:['list'] } var AppComponent={ template:` <div class="container"> <search @onsearch="search($event)" ></search> <list :list="datas" ></list> </div> `, components:{ 'list':ListComponent, 'search':SearchComponent }, methods:{ search:function($e){ this.datas=JSON.stringify({ data:[], info:'info' }); } }, data:function(){ return { datas:null } } } var app=new Vue({ el:'#app', components:{ 'app':AppComponent } }); </script> </body> </html>
點擊搜索按鈕,運行效果如下:
上面的例子非常簡單,而且所寫代碼在前面的博文中都有所介紹,這里就不詳述了,在這里數據流流向如下:
1、點擊按鈕,數據由 search組件流向父組件
2、父組件監聽onsearch ,監聽到事件后,處理並給list賦值,此時數據由 父組件 流向 list組件
父組件這里的作用就是一個中轉站,提供了一種數據流的中轉功能。那么如果沒有父組件,能否實現上述功能呢,畢竟我們不可能每次兄弟組件通信都創建一個多余父組件過來,這樣如果嵌套層數過多也是很大的問題,對於兄弟組件通信的問題,官方也提到了叫做event bus的實現方式,下面我們就實現一下第二種方案,基於event bus:
修改我們的代碼如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>demo4</title> <script src="https://cdn.bootcss.com/vue/2.4.1/vue.js"></script> </head> <body> <div id="app"> <search></search> <list></list> </div> <script> var eventBus = new Vue(); var SearchComponent = { template: ` <div class="toolbar"> <input type="text" placeholder="keyword" v-model="keyword"/> <input type="text" placeholder="description" v-model="desc"/> <input type="button" value="search" @click="search()" /> </div> `, data: function () { return { keyword: '', desc: '' } }, methods: { search: function () { // this.$emit('onsearch',{keyword:this.keyword,desc:this.desc}); eventBus.$emit('onsearch', { keyword: this.keyword, desc: this.desc }); } } } var ListComponent = { template: ` <div class="list" > {{list}} </div> `, data: function () { return { list: null } }, created: function () { var self = this; eventBus.$on('onsearch', function ($e) { console.log($e); self.list = JSON.stringify($e); }) } } var app = new Vue({ el: '#app', components: { // 'app':AppComponent 'list': ListComponent, 'search': SearchComponent } }); </script> </body> </html>
這里借助一個全局的vue空實例,來實現一個全局的eventbus,當然我們也可以使用或者實現自己的eventbus,這個是比較簡單的,(大致思路是:定義一個回調列表數組,定義兩個方法,一個on一個emit,on即是向回調數組push key 和對應的function,emit就是觸發key對應的function)有興趣的可以簡單做一下,保存后運行即可。
對於簡單的兄弟組件通信,其實這種方案或者第一種方案已經滿足,但是如果兄弟節點過多或者組件層次很深的時候,使用第一種方案我們必須一層一層的傳遞幾乎重復的代碼,使用第二種方案所有組件又全部依賴於全局vue實例或者說全局eventbus,有沒有更好的狀態管理方案呢?能否把狀態管理獨立出來呢,這就是我們接下來要說的vuex。
每一個 Vuex 應用的核心就是 store(倉庫)。"store" 基本上就是一個容器,它包含着你的應用中大部分的狀態(state)。Vuex 和單純的全局對象有以下兩點不同:
-
Vuex 的狀態存儲是響應式的。當 Vue 組件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那么相應的組件也會相應地得到高效更新。
-
你不能直接改變 store 中的狀態。改變 store 中的狀態的唯一途徑就是顯式地提交(commit) mutations。這樣使得我們可以方便地跟蹤每一個狀態的變化,從而讓我們能夠實現一些工具幫助我們更好地了解我們的應用。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>demo4</title> <script src="https://cdn.bootcss.com/vue/2.4.1/vue.js"></script> <script src="https://cdn.bootcss.com/vuex/2.3.1/vuex.js"></script> </head> <body> <div id="app"> <search></search> <list></list> </div> <script> // var eventBus = new Vue(); var store = new Vuex.Store({ state: { list: null }, mutations: { search: function (state, payload) { state.list = JSON.stringify(payload); } } }) var SearchComponent = { template: ` <div class="toolbar"> <input type="text" placeholder="keyword" v-model="keyword"/> <input type="text" placeholder="description" v-model="desc"/> <input type="button" value="search" @click="search()" /> </div> `, data: function () { return { keyword: '', desc: '' } }, methods: { search: function () { this.$store.commit("search",{ keyword: this.keyword, desc: this.desc }) //eventBus.$emit('onsearch', { keyword: this.keyword, desc: this.desc }); } } } var ListComponent = { template: ` <div class="list" > {{list}} </div> `, computed:{ list:function(){ return this.$store.state.list; } } } var app = new Vue({ el: '#app', store:store, components: { // 'app':AppComponent 'list': ListComponent, 'search': SearchComponent } }); </script> </body> </html>
這里我們創建了一個全局store,store是唯一的,里面保存着所有的狀態(這種狀態建議是全局的或者共享的,我們這里假設list組件中的state屬於共享,大家不要較真,而search中的state屬於組件本身狀態),我們做如下約定:不要直接修改狀態,要通過提交mutations來修改狀態,mutations相當於在react中使用setState去修改狀態一樣。直接修改會運行時異常。
針對上面的代碼,主要包括如下幾個知識點:
1、vuex的實例化:直接new Vuex.Store ,創建全局唯一store,通過配置參數,設置state(全局共享的)、mutations(只支持同步操作)
2、vuex和vue的聯系,通過new Vue實例時,注入store,這里和前文中注入router類似,注入后,在任何子組件中,就可以通過this.$store來訪問store了
3、store中的state是響應式的,所以建議定義為組件計算屬性,每次通過mutations提交修改,則可直接驅動view的變化。
本節主要引入vuex,算是vuex的開篇,不介紹過多內容,讓我們有一個簡單的認識,接下來會向介紹vue-router一樣,慢慢的深入其它的方方面面。敬請期待。