Vue 深入学习之组件通信

栏目: JavaScript · 发布时间: 7年前

内容简介:组件通信的场景可以分为:父子组件通信、非父子组件通信。父子组件通信又可以分为单层父子组件通信和多层级父子组件通信。多层父子组件通信是在单层的基础上进行通信,先来看看单层父子组件通信的实现。按数据流向不同,把父子组件拆分为父组件向子组件通信、子组件向父组件通信两部分主要有如下方式:

组件通信的场景可以分为:父子组件通信、非父子组件通信。父子组件通信又可以分为单层父子组件通信和多层级父子组件通信。

父子组件

多层父子组件通信是在单层的基础上进行通信,先来看看单层父子组件通信的实现。按数据流向不同,把父子组件拆分为父组件向子组件通信、子组件向父组件通信两部分

父组件向子组件通信

主要有如下方式:

  • $props$attrs$listeners
  • $children$refs
  • provide/inject

关于 $props$attrs$listeners

child.vue

<template>
  <div>
    {{ label }}: <input v-bind="$attrs" v-on="$listeners" v-model="age">
  </div>
</template>

<script>
  export default {
    name: 'child',
    props: {
      label: String
    },
    data () {
      return {
        age: ''
      }
    }
  }
</script>

parent.vue

<template>
  <child :label="label" type="number" @blur="onBlur"></child>
</template>

<script>
  import Child from './child'
  export default {
    name: 'parent',
    components: {
      Child
    },
    data () {
      return {
        label: '年龄'
      }
    },
    methods: {
      onBlur (e) {
        console.log(e.target.value)
      }
    }
  }
</script>

上面的例子中, parent.vuechild.vue 传递了3个值 labeltypeblur 监听事件,子组件分别通过 $props$attrs$listeners 可以取到从父组件传递过来的值。其中, child.vue$props$attrs 的不同在于是否在 child.vueprops 中定义,使用不同之处参考 文档

关于 $children

可以直接在父组件中访问子组件,得到的是一个数组。如果要访问指定子组件,可以使用 $refs

当前实例的直接子组件。 需要注意 $children 并不保证顺序,也不是响应式的。

关于 provide/inject

这对选项需要一起使用,给所有后代组件注入一个依赖,所以也可以用于多层组件通信的场景。

provide/inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。

子组件向父组件通信

主要有如下方式:

$emit
$parent
props

关于 $emit

child.vue

<template>
  <button @click="onClick">按钮{{ num }}</button>
</template>

<script>
  export default {
    name: 'child',
    props: {
      num: {
        type: Number,
        default: 0
      }
    },
    methods: {
      onClick () {
        this.$emit('btn-click', 'hi')
        this.$emit('update:num', this.num + 1)
      }
    }
  }
</script>

parent.vue

<template>
  <child @btn-click="handleClick" :num.sync="num"></child>
</template>

<script>
  import Child from './child'
  export default {
    name: 'parent',
    components: {
      Child
    },
    data () {
      return {
        num: 0
      }
    },
    methods: {
      handleClick (msg) {
        console.log(msg)
      }
    }
  }
</script>

上面的例子中, child.vue 通过 $emit 发送 btn-click 事件, parent.vue 通过监听 btn-click 事件获取到值。如果父组件通过 props 传递的值包含 .sync 修饰符 ,子组件可以直接通过 $emit('update:propName') 的方式更新父组件的值,如上例的 num

关于 $parent

可以直接在子组件中访问父组件,得到的是一个父组件的实例

关于通过 props 修改父组件数据

如果父组件传递给子组件的 props 是一个引用类型时,在不修改引用地址的情况下,修改子组件的 props 的数据即修改父组件的数据。

但是 不推荐 这么做,直接修改的方式会造成数据流的混乱。实际使用可以通过将数据拷贝一份或者特定接口来更新数据。

多层父子组件通信

Vue在2.0版本中去除了 $broadcast 方法以及 $dispatch 方法,常见多层组件通信通常是在单层的基础上进行通信,如父级的父级,可以通过 vm.$parent.$parent 访问,同理,子级的子级可以用 vm.$children[index].$children[index] 的方式。

下面是 element-ui 的实现,以minix的方式引入

function broadcast(componentName, eventName, params) {
  this.$children.forEach(child => {
    var name = child.$options.componentName;

    if (name === componentName) {
      child.$emit.apply(child, [eventName].concat(params));
    } else {
      broadcast.apply(child, [componentName, eventName].concat([params]));
    }
  });
}
export default {
  methods: {
    dispatch(componentName, eventName, params) {
      var parent = this.$parent || this.$root;
      var name = parent.$options.componentName;

      while (parent && (!name || name !== componentName)) {
        parent = parent.$parent;

        if (parent) {
          name = parent.$options.componentName;
        }
      }
      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params));
      }
    },
    broadcast(componentName, eventName, params) {
      broadcast.call(this, componentName, eventName, params);
    }
  }
};

broadcast 通过递归遍历子组件找到所需组件进行广播事件, dispatch 逐级向上查找对应父组件的派发事件。 broadcast 根据 componentName 深度遍历子组件找到对应组件 emit 事件 eventName ,并传递 paramsdispatch 根据 componentName 向上级查找对应父组件, emit 事件 eventName ,并传递 params

非父子组件通信

非父子组件通信主要有两种方式,一是通过共同的父级页面转换成父子通信,二是 event bus 。通过共同的父级页面转换成父子通信的方式跟上面介绍的类似,一下主要介绍下 event bus :

Vue 内部有一个事件机制, $on 方法用来监听一个事件, $emit 用来触发一个事件。

// 建立一个Vue实例作为中央事件总线
let event = new Vue()

// 监听事件
event.$on('eventName', (value) => {
  // do something
})

// 触发事件
event.$emit('eventName', 'value')

Vuex

vuex 可以满足上面的大多数需求,但通常它不是必须的。

如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的

当应用足够复杂时,上面的一些方式,如 event bus 可能并不能很好的处理好各种数据之间的流转, vuex 也许是更好的方式。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Spring Into HTML and CSS

Spring Into HTML and CSS

Molly E. Holzschlag / Addison-Wesley Professional / 2005-5-2 / USD 34.99

The fastest route to true HTML/CSS mastery! Need to build a web site? Or update one? Or just create some effective new web content? Maybe you just need to update your skills, do the job better. Welco......一起来看看 《Spring Into HTML and CSS》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具