看完这篇文章,你将了解:

  • 为什么要使用组件?
  • 全局注册和局部注册组件
  • 父组件如何给子组件传递数据?
  • 子组件如何给父组件传递数据?
  • 非父子组件如何通信?
  • 什么是单向数据流?单向数据流的两种应用常景

为什么要使用组件?

组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以表现为用 is 特性进行了扩展的原生 HTML 元素。所有的 Vue 组件同时也都是 Vue 的实例,所以可接受相同的选项对象 (除了一些根级特有的选项) 并提供相同的生命周期钩子。

全局注册和局部注册组件

组件两要素: 组件名模板

  • 全局注册

    Vue.component('component', {
      template: '<div>我是一个全局组件</div>'
    })
  • 局部注册

    var app = new Vue({
        el: '#app',
        components: {
            'component': {
                template: '<div>我是一个局部注册组件</div>'
            }
        }
    })
  1. 全局注册: Vue.component(组件名, 模板)
  2. 局部注册: components: {组件名: 模板}

父组件如何给子组件传递数据?

在组件的属性上实现

  1. 子组件设置属性 xx=父组件信息
  2. 子组件的 props 变量接收 xx

props 译为属性,组件中的 props 变量定义的就是组件作为 html 标签的属性数组
之所以用属性传递数据,是因为从 html 角度上讲,子组件属性是子组件暴露给父组件的数据接口

<div id='app'>
  <com xx='msg'></com>
</div>
var app = new Vue({
    el: 'app',
    data: {
        msg: '来自父组件的信息'
    },
    components: {
        'com': {
            template: '<div>{{xx}}</div>',
            props: ['xx']
        }
    }
})

理论上,也可以通过以下方式拿到父组件数据
this.$parent.父亲数据
this.$root.父亲数据 (若 root 是父亲)

子组件如何给父组件传递数据?

自定义事件实现
有三个关键部分:

  1. 子组件绑定自定义事件(这是一个桥梁,搭完后两段施工)
    <com @事件名 = 方法名></com>
  2. 子组件端触发事件名
  methods: 
  ...
  this.$emit(事件名, this.message)
  ...
  1. 父组件端注册方法名
  methods:
  ...
  方法名: function(value) {}
  ...

这样,子组件的数据 this.message 就成为了父组件方法参数中的 value
demo

理论上,也可以通过以下方式拿到子组件数据
<com ref='a'>, 然后 this.$ref.a.子组件数据

非父子组件如何通信?

借助公共父组件通信(类似于 eventHub 模块)

  1. 父组件搭建事件中心
  bus: new Vue()
  1. 发送者访问父亲的 bus,发送事件
  this.$root.bus.$emit(xx, 数据)
  1. 接受者访问父亲的 bus,注册事件
  this.$root.bus.$on(xx, 动作)

demo

单向数据流

  • 解释:也就是父组件数据变化时会传递给子组件,但是反过来不行。比如通过 props 传递数据就是单向。
  • 目的:是尽可能将父子组件解耦,避免子组件无意间修改了父组件的状态
  • 应用场景:业务中经常遇到两种需要改变(使用) prop 的情况
  1. 一种是父组件传递初始值进来,子组件将它作为初始值保存起来,在自己的作用域下可以随意使用和修改。这种情况可以在组件 data 内再声明一个数据,引用父组件数据
Vue.component('my-component', {
    props: ['msg'],
    template: '<div>{{count}}</div>',
    data: function() {
        return {
            count: this.msg
        }
    }
})
  1. 另一种是 props 作为需要被转变的原始值传入。这种情况用计算属性就可以了
Vue.component('width-component', {
    props: ['width'],
    template: '<div :style="style"></div>'
    computed: {
        style: function() {
            return {
                width: this.width + 'px',
                background: 'red',
                height: '30px'
            }
        }
    }
})
<div id='app'>
  <input type="text" v-model="width">
  <width-component :width="width"></width-component>
</div>