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

相关文章
相关标签/搜索