Vue組件通訊深刻

上一篇:vue生命週期深刻
下一篇:Vue組件通訊深刻Vuexjavascript

建議:博客中的例子都放在vue_blog_project工程中,推薦結合工程實例與博客一同窗習css

vue中,組件是帶有一個名字、可複用的 Vue 實例。因爲 Vue 是面向視圖的MVVM框架, 組件能夠看作是對數據和方法的簡單封裝、具備獨立的邏輯和功能的界面,多個組件按照必定規則的組合最終成爲一個完整的應用

1. 組件的註冊

1.1 全局註冊

Vue.component()用來建立全局組件,一旦註冊,便可在該實例Vue下的任何子組件中使用,經常使用於一些使用較爲頻繁的基礎組件,如Alert組件、Button組件、佈局組件等html

使用方式:vue

Vue.component('my-component', {
    // vue實例方法和生命週期(el除外)
})

若是你使用過 element-ui ,下面的寫法你可能比較熟悉:java

import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';

Vue.use(ElementUI);

new Vue({
    el: '#app',
    render: h => h(App)
});

其中Vue.use(ElementUI);的方式即是間接調用了全局組件註冊的方式,在element-ui內部:
(插件中,使用Vue.use()的方式,至關於調用了其中的install方法)webpack

const install = function(Vue, opts = {}) {
// ...
components.map(component => {
    Vue.component(component.name, component);
});
// ...
};

能夠看出,在其內部也是依次全局註冊了element中的插件git

1.2 局部註冊

Vue官網上如是說:github

全局註冊每每是不夠理想的。好比,若是你使用一個像 webpack 這樣的構建系統,全局註冊全部的組件意味着即使你已經再也不使用一個組件了,它仍然會被包含在你最終的構建結果中。這形成了用戶下載的 JavaScript 的無謂的增長。

正是由於上面的緣由,除了一些經常使用的基礎組件外,儘量的使用局部註冊的方式web

// 普通引入方式
var ComponentA = { /* ... */ }

// ES6引入方式
import ComponentA from './ComponentA.vue'

export default {
    // ...
    components: {
        'component-a': ComponentA,
    }
}

值得注意的是:局部註冊方式僅能在當前組件中使用,在其子組件中使用須要再次註冊element-ui

2. 組件的組織

上面提到,多個組件按照必定規則的組合最終成爲一個完整的應用,所以,咱們能夠將組件看做是Vue頁面中的最小單元,那麼應該如何組織組件,整合成一個頁面呢?

有這樣一個需求:要求按照下圖組織頁面結構

clipboard.png

咱們能夠這樣組織:

<template>
    <div class="m-body">
    我是主體內容
    </div>
</template>

<script>
export default {}
</script>

<style scoped>
.m-body {
    min-height: 500px;
    color: #fff;
    padding: 20px;
    background-color: #39f;
}
</style>

按照這種方式,依次寫出header、aside、content、footer四個組件,並用一個組件做爲這四個組件的父組件來組織頁面結構,最後的結構以下:

clipboard.png

父組件以下:

<template>
    <div class="comp">
    <m-header />
    <div class="main">
        <m-side />
        <m-body />
    </div>
    <m-footer />
</div>
</template>

<script>
import MHeader from './MHeader'
import MFooter from './MFooter'
import MBody from './MBody'
import MSide from './MSide'

export default {
    components: {
        MHeader,
        MFooter,
        MBody,
        MSide
    }
}
</script>

<style lang="scss" scoped>
.main {
    margin: 10px 0;
    display: flex;
    .m-side {
        width: 200px;
        margin-right: 10px;
    }
    .m-body {
        flex: 1;
    }
}
</style>

打開Vue調試界面,將看到以下的結構

clipboard.png

注意:父組件負責控制容器結構樣式(各個直接子組件的位置、大小等),子組件負責其內部的樣式,不要在子組件中寫本身的容器樣式

3. 組件之間的數據傳遞

組件的組合僅僅只是將頁面結構搭建了起來,要完成頁面的交互功能,組件之間一定會有數據傳遞
按照頁面結構,大致上能夠將組件間的數據傳遞分紅兩種:

  1. 父子組件間的數據傳遞
  2. 兄弟組件間的數據傳遞
  3. 非直接關聯性組件間的數據傳遞

3.1 組件間簡單的數據通訊

Vue官網中對props、$emit、slot有很是詳細的描述,在此再也不嘮述

現有新的 需求:在上面例子的基礎上,須要知足:header中有一個數值,side中新增重置和增長按鈕,body中新增數組輸入框,當對按鈕和表單做操做時,對應的數值做相應改變

clipboard.png

基本思路:將數值放在幾個組件公共上層組件中,header中prop接受該值,side和body中點擊按鈕向他們的公共上層組件分發$emit事件,改變該數值,核心思路:多個組件操做的值均爲上層組件的變量

代碼以下:

(1)父級組件:主要用於數據傳遞與接收子組件分發的事件來改變對應的變量

<div class="comp">
    <m-header :num="num" />
    <div class="main">
        <m-side @add="handleAdd" @reset="handleReset" />
        <m-body :num="num" @change="handleChange" />
    </div>
    <m-footer />
</div>
export default {
    data () {
        return {
            num: 0
        }
    },
    methods: {
        handleAdd () {
            this.num += 1
        },
        handleChange (val) {
            this.num = val
        },
        handleReset () {
            this.num = 0
        }
    },
    // ...
}

(2)Header組件:接受並展現數值
template中僅添加{{ num }}

props: {
    num: {
        type: Number,
        default: 0
    }
}

(3)Side組件:向上分發增長和重置事件

<!-- 新增 -->
<el-button @click="add">ADD</el-button>
<el-button @click="reset">RESET</el-button>
methods: {
    add () {
        this.$emit('add')
    },
    reset () {
        this.$emit('reset')
    }
}

(4)Body組件:監控傳值,向上分發事件

<!-- 新增 -->
<el-input-number v-model="currentVal" @change="handleChange"></el-input-number>
props: {
    num: {
        type: Number,
        default: 0
    }
},
data () {
    return {
        currentVal: 0
    }
},
// 外層數據改變時,currentVal值須要同步修改
watch: {
    num: {
        handler (val) {
            this.currentVal = val
        },
        immediate: true
    }
},
methods: {
    handleChange (val) {
        this.$emit('change', val)
    }
}

這種簡單的數據交互使用prop和$emit足以應付,可是
(1)對於深層組件嵌套中的數據傳遞,使用這種通訊方式則須要一層一層向下prop,改變時須要一層一層向上$emit
(2)對於兄弟組件之間的數據傳遞,先要向上分發,再向下prop,過於繁瑣且不易監控調試

這裏有一個 新的需求:在最初組件組合的基礎上,side組件中有一個數據展現,要求經過body中深層嵌套的組件操做以改變side中的數據

修改:在body組件中添加<slot></slot>,並新增一個組件掛載在該插槽上,用以模擬深層嵌套(固然了,實際的工做中的嵌套可能涉及到四層甚至更多)

3.2 $root方式

上面方法的核心是全部子組件統一管理和操做父組件的數據,子組件負責展現和分發事件,實際操做值的始終在父組件,Vue提供了一個能訪問到根組件的方法,官網中如是描述:處理邊界狀況中訪問根實例部分

(1)在入口文件main.js中添加:

new Vue({
    data: {
        rootNum: 0
    },
    // ...
})

(2)在父組件中添加:

<!-- 局部註冊不做詳述 -->
<m-body>
    <m-body-item></m-body-item>
</m-body>

(3)新添加的組件MBodyItem

<template>
<div class="m-body-item">
    <el-button @click="add">ADD</el-button>
    <el-button @click="reset">RESET</el-button>
</div>
</template>

<script>
// 可直接操做$root中聲明的變量
export default {
    methods: {
        add () {
            this.$root.rootNum += 1
        },
        reset () {
            this.$root.rootNum = 0
        }
    }
}
</script>

(4)side組件

<div class="m-side">
    我是側邊欄{{ $root.rootNum }}
</div>

對於 demo 或很是小型的有少許組件的應用來講直接使用$root的方式很方便。不過這個模式擴展到中大型應用來講就否則了,數據量過大不易維護,也不易追蹤數據的變化

3.3 總線Bus方式

總線Bus的思路:將事件的註冊和觸發單獨放在一個Vue實例中,點擊按鈕時觸發指定的事件以驅動接下來的操做。Bus總線僅僅是用來驅動事件的,具體的數據操做仍是在原有的組件中

在$root的結構基礎上,做以下更改:
(1)原入口文件main.js還原,去掉data屬性
(2)新定義一個總線文件bus.js

import Vue from 'vue'
export default new Vue()

(3)side組件中註冊總線事件並顯示數據

import Bus from './bus'
export default {
    data () {
        return {
            sideNum: 0
        }
    },
    created () {
        Bus.$on('change', (step) => {
            this.sideNum += step
        })
        Bus.$on('reset', () => {
            this.sideNum = 0
        })
    }
}

(4)bodyItem組件中分發總線事件

import Bus from './bus'
export default {
    methods: {
        add () {
            Bus.$emit('change', 1)
        },
        reset () {
            Bus.$emit('reset')
        }
    }
}

總線的方式,將原有的數據傳遞轉換成了事件驅動的形式,這一點規避了組件層級的嵌套問題,可是開發人員沒法追蹤調試數據

3.4 Vuex方式

因爲內容較多,將在下一篇博客中詳細介紹,敬請期待..

上一篇:vue生命週期深刻
下一篇:Vue組件通訊深刻Vuex

相關文章
相關標籤/搜索