一步一步學習Vue(十)


本篇說一下組件通信的問題,父子組件通信,前面的博客中已有說明,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 是一個專為 Vue.js應用程序開發的狀態管理模式。它采用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。Vuex 也集成到Vue 的官方調試工具 devtools extension,提供了諸如零配置的 time-travel調試、狀態快照導入導出等高級調試功能,如果打開官網,你會看到上面這段話。
這都是什么亂七八糟的,可能讓人看不明白,雖然看起來逼格很高,其實它只是做的比我們的eventbus 更高級一點:

每一個 Vuex 應用的核心就是 store(倉庫)。"store" 基本上就是一個容器,它包含着你的應用中大部分的狀態(state)。Vuex 和單純的全局對象有以下兩點不同:

  1. Vuex 的狀態存儲是響應式的。當 Vue 組件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那么相應的組件也會相應地得到高效更新。

  2. 你不能直接改變 store 中的狀態。改變 store 中的狀態的唯一途徑就是顯式地提交(commit) mutations。這樣使得我們可以方便地跟蹤每一個狀態的變化,從而讓我們能夠實現一些工具幫助我們更好地了解我們的應用。

vuex中包含很多概念和約定,今天我們就開始體驗一下,話不多說,同樣的功能基於vuex重構一下:
<!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一樣,慢慢的深入其它的方方面面。敬請期待。

 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM