Six ways inter-component communication Vue

Foreword

Vue.js component is one of the most powerful features, while the component instance scope is independent of each other, which means between the different components of the data can not reference each other. In general, the components can have the following relationship:

 

 

As shown above, A and B, B and C, B and D are paternity, C and D are brothers, A, and C is a generational relation (probably multi-generational partition). The use of different scenarios, how to choose effective means of communication?

Method, props / $ emit

A parent component B is transmitted to the sub-assembly by way of props.

1. The value passed to the parent component subassembly

Next we through an example of how a parent component transfer assembly as He Xiangzai Found: users how to get the data in the parent components App.vue subassembly Users.vue in: [ "Henry", "Bucky", "Emily"]

//App.vue父组件
<template>
  <div id="app">
    <users v-bind:users="users"></users>//前者自定义名称便于子组件调用,后者要传递数据名
  </div>
</template>
<script>
import Users from "./components/Users"
export default {
  name: 'App', data(){ return{ users:["Henry","Bucky","Emily"] } }, components:{ "users":Users } } 复制代码
//users子组件
<template>
  <div class="hello">
    <ul>
      <li v-for="user in users">{{user}}</li>//遍历传递过来的值,然后呈现到页面
    </ul>
  </div>
</template>
<script>
export default {
  name: 'HelloWorld',
  props:{
    users:{           //这个就是父组件中子标签自定义名字
      type:Array, required:true } } } </script> 复制代码

Summary: parent component subassembly to pass data down through the props. Note: There are three components in the form of data: data, props, computed

2. The sub-components by value to the parent component (through events form)

Next, we use an example of how to pass values ​​to the parent sub-assembly components: When we click on "Vue.js Demo", sub-assemblies pass a value to the parent component, the text from the "transfer of value is a" becomes " to the parent sub-assembly by value "transmitted to the parent sub-assembly to achieve component values.

// 子组件
<template>
  <header>
    <h1 @click="changeTitle">{{title}}</h1>//绑定一个点击事件
  </header>
</template>
<script>
export default {
  name: 'app-header',
  data() { return { title:"Vue.js Demo" } }, methods:{ changeTitle() { this.$emit("titleChanged","子向父组件传值");//自定义事件 传递值“子向父组件传值” } } } </script> 复制代码
// 父组件
<template>
  <div id="app">
    <app-header @titleChanged="updateTitle" ></app-header>//与子组件titleChanged自定义事件保持一致
   // updateTitle($event)接受传递过来的文字
    <h2>{{title}}</h2>
  </div>
</template>
<script>
import Header from "./components/Header"
export default { name: 'App', data(){ return{ title:"传递的是一个值" } }, methods:{ updateTitle(e){ //声明这个函数 this.title = e; } }, components:{ "app-header":Header, } } </script> 复制代码

Summary: subcomponents to send a message to the parent component by events, in fact, sub-assemblies to send their data to the parent component.

Second method, the central event bus EventBus

For communication, the above components of the way between father and son is entirely achievable, but for the two components are not father and son relationship, then how should communicate it? In the case of small-scale projects, you can use the central event bus EventBus way. If your project is large and medium scale, then you can use Vuex state management to be introduced behind us. Introduction of EventBus EventBus also known as event bus. EventBus can be used in the Vue as a bridge concept is the same event as the center of all common components, you can register to receive or send events to the event center, so components can be up and down parallel to notify other components, but it is too so easy to use, if inadvertently, it will make it difficult to maintain the disaster, hence the need for better Vuex as state management center, the concept will be notified rise to shared state level.

How to use EventBus how to use EventBus in Vue project to achieve data communication between the components of it? DETAILED can be accomplished by the following steps.

Initialization The first thing you need to do is create an event bus and export it to other modules or can be used to listen to it. We can be handled in two ways. First look at the first, to create a new .js file, such as eventBus.js:

// eventBus.js
import Vue from 'vue'
export const EventBus = new Vue()
复制代码

You need to do is to introduce Vue and export it an instance (in this case, I call it EventBus). It is essentially a component does not have the DOM, it's just that it has only an instance method, so it is very light. Another way can be directly in the project main.js initialization EventBus:

// main.js
Vue.prototype.$EventBus = new Vue()
复制代码

Note that this is a way to initialize EventBus global event bus and now we have created EventBus, then you need to do is load it in your component, and call the same way as you father and son in the assembly as each message is transmitted.

Example: the trigger A.vue

import EventBus from 'eventBus.js'; 
// 必须引入同一个实例

methods: {
    doSomething() {
        EventBus.$emit("getTarget", 22); console.log("向getTarget方法传参22"); } } 复制代码

B.vue in a listening event

import EventBus from 'eventBus.js'; 
// 我们在create钩子中监听方法
create(){
    console.log("this.getTarget是一个带参数的方法,但是这边只要将两者关联起来");
    EventBus.$on('getTarget', this.getTarget); }, beforeDestroy() { console.log("组件销毁前需要解绑事件。否则会出现重复触发事件的问题"); EventBus.$off('getTarget', this.getTarget); }, methods: { getTarget(param) { // todo } } 复制代码

eventBus actually very convenient, any of the components of communication can be accomplished with it. However, we will select the mode depending on the situation easier maintenance. Because eventBus relatively poor listeners find the corresponding place or event triggers specific implementation, it is generally considered more communication components above implementation. Communicating between EventBus use, and inside the module, using vuex communication, maintenance data, will be clearer in the logic.

Method three, vuex

 

1. brief introduction Vuex principle

 

Vuex implements a one-way data flow, data stored in the State has a global, when the components you want to change the data in the State, must be carried out by Mutation, Mutation while providing subscribers mode for external plug-in call to get updated State data. The data of the State when all asynchronous operations (common in backend interface asynchronous call to get an update) or batch synchronization operations need to take Action, but also can not be modified State Action directly, or need to be modified by Mutation. Finally, according to the change of the State, to render the view.

2. The functions of each module in the overview of the process:

Vue Components: Vue components. HTML page, interactions responsible for receiving user operation, execute dispatch method triggers a corresponding action in response.

dispatch: operating behavior trigger method is the only method capable of performing the action.

actions: operational behavior processing module, triggered by assembly $ store.dispatch ( 'action name', data1). Then the commit () call to trigger mutation, indirect update state. Responsible for handling all interactions Vue Components received. Comprising synchronous / asynchronous operation, support the methods of the same name, which in turn triggers the order of registration. Request to the backend API operations on in this module, including trigger other action and submit the operation mutation. This module provides Promise package to support the action triggered a chain.

commit: commit operation state change method. Submit to mutation, it is the only way to perform mutation of.

mutations:状态改变操作方法,由actions中的 commit('mutation 名称')来触发。是Vuex修改state的唯一推荐方法。该方法只能进行同步操作,且方法名只能全局唯一。操作之中会有一些hook暴露出来,以进行state的监控等。

state:页面状态管理容器对象。集中存储Vue components中data对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用Vue的细粒度数据响应机制来进行高效的状态更新。

getters:state对象读取方法。图中没有单独列出该模块,应该被包含在了render中,Vue Components通过该方法读取全局state对象。

3.具体使用:

store/index.js

import Vuex from 'vuex';
import Vue from 'vue';
Vue.use(Vuex);
const store = new Vuex.Store({
    state: {
        stateName: 'xxxx'
    },
    mutations: {
       mutationsName(state, {params}) {
           state.stateName = params;
            console.log("只有在mutations中才能直接改变state中的值")
       } 
    },
    actions: {
        actionName({ state,commit}, {params}) {
        let actionParam = 'mmm'; commit('mutationsName', actionParam ); console.log(" 触发mutation 方法要用commit分发,以此改变state"); } } }); export default store; 复制代码

main.js

import store from './store/index.js';
new Vue({
  el: '#app',
  store,
  console.log("将store挂载到vue实例上")
  render: h => h(App)
})
复制代码

在组件中使用

child.vue js部分

**import { mapActions, mapMutations, mapState } from 'vuex';
export default {
    computed: {
        ...mapState({ stateName })
    },
    methods: {
        ...mapActions(['actionName']),
        ...mapMutations(['mutationName'])
        console.log("使用辅助函数mapMutations直接将触发函数映射到methods上") } // 接下来在实例中就可以用this.stateName,this.actionName来调用 } 复制代码

当兄弟组件很多,涉及到的处理数据庞大的时候,可以用到vuex中的modules,使得结构更加清晰

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
复制代码

方法四、 attrs/ listeners

1.简介

多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。但如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,未免有点大材小用。 父子组件之间的数据传输有一个问题:如果父组件A下面有子组件B,组件B下面有组件C,这时如果组件A想传递数据给组件C怎么办呢? 如果采用props方法,我们必须让组件A通过prop传递消息给组件B,组件B在通过prop传递消息给组件C;要是组件A和组件C之间有更多的组件,那采用这种方式就很复杂了。Vue 2.4开始提供了attrs和listeners来解决这个问题,能够让组件A之间传递消息给组件C。

官网对 $attrs 的解释如下:

$attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 interitAttrs 选项一起使用。
复制代码

官网对 $listeners 的解释如下:

$listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
复制代码

以上是官网对$attrs的解释,我刚看我也是一脸懵逼,回去试了一下其实并不难,而且比较适用组件深层嵌套场景下,祖先组件向子组件传值的问题

我的理解:

意思就是父组件传向子组件传的,子组件不prop接受的数据都会放在$attrs中,子组件直接用this.$attrs获取就可以了。如过从父->孙传,就在子组件中添加v-bind='$attrs',就把父组件传来的子组件没props接收的数据全部传到孙组件;我觉得 $attrs 和 $listeners 属性像两个收纳箱,一个负责收纳属性,一个负责收纳事件,都是以对象的形式来保存数据; 复制代码

接下来我们看个跨级通信的例子:

// index.vue
<template>
  <div>
    <h2>科大讯飞</h2>
    <child-com1
      :foo="foo"
      :boo="boo"
      :coo="coo"
      :doo="doo"
      @one.native="triggerOne" @two="triggerTwo" title="Vue通信" ></child-com1> </div> </template> <script> const childCom1 = () => import("./childCom1.vue"); export default { components: { childCom1 }, data() { return { foo: "Javascript", boo: "Html", coo: "CSS", doo: "Vue" }; }, methods:{ triggerOne(){ alert('one') }, triggerTwo(){ alert('two') } } }; </script> 复制代码
// childCom1.vue
<template class="border">
  <div>
    <p>foo: {{ foo }}</p>
    <p>childCom1的$attrs: {{ $attrs }}</p>
    <child-com2 v-bind="$attrs" v-on="$listeners"></child-com2> </div> </template> <script> const childCom2 = () => import("./childCom2.vue"); export default { components: { childCom2 }, // 默认为true,如果传入的属性子组件没有prop接受,就会以字符串的形式出现为标签属性 // 设为false,在dom中就看不到这些属性,试一下就知道了 inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性 props: { foo: String // foo作为props属性绑定 }, mounted() { console.log(this.$attrs); // { "boo": "Html", "coo": "CSS", "doo": "Vue", "title": "Vue通信" } console.log(this.$listeners); } }; </script> 复制代码
// childCom2.vue
<template>
  <div class="border">
    <p>boo: {{ boo }}</p>
    <p>childCom2的$attrs: {{ $attrs }}</p>
    <child-com3 v-bind="$attrs" v-on="$listeners"></child-com3> </div> </template> <script> const childCom3 = () => import("./childCom3.vue"); export default { components: { childCom3 }, inheritAttrs: false, props: { boo: String }, mounted() { console.log(this.$attrs); // {"coo": "CSS", "doo": "Vue", "title": "Vue通信" } console.log(this.$listeners); } }; </script> 复制代码
// childCom3.vue
<template>
  <div class="border">
    <p>childCom3: {{ $attrs }}</p>
  </div>
</template>
<script>
export default {
  props: {
    coo: String,
    title: String
  },
  mounted() { console.log(this.$listeners); // this.$listeners.two(); } }; </script> 复制代码

 

 

$attrs表示没有继承数据的对象,格式为{属性名:属性值}。Vue2.4提供了 $attrs , $listeners 来传递数据与事件,跨级组件之间的通讯变得更简单。
简单来说: $attrs与 $listeners 是两个对象, $attrs 里存放的是父组件中绑定的非 Props 属性, $listeners里存放的是父组件中绑定的非原生事件。 复制代码

方法五、 provide/inject (底层的通用组件时,使用频率会很高)

1.简介

Vue2.2.0新增API,这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。一言而蔽之:祖先组件中通过provider提供变量,然后在子孙组件中通过inject来注入变量。provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。 来看下官方对 provide / inject 的描述: provide 和 inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。并且这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。

2.举个例子

假设有两个组件: A.vue 和 B.vue,B 是 A 的子组件

// A.vue
export default {
  provide: {
    name: '科大讯飞'
  }
}
复制代码
// B.vue
export default {
  inject: ['name'],
  mounted () {
    console.log(this.name);  // 科大讯飞
  }
}
复制代码

可以看到,在 A.vue 里,我们设置了一个 provide: name,值为 科大讯飞,它的作用就是将 name 这个变量提供给它的所有子组件。而在 B.vue 中,通过 inject 注入了从 A 组件中提供的 name 变量,那么在组件 B 中,就可以直接通过 this.name 访问这个变量了,它的值也是 科大讯飞。这就是 provide / inject API 最核心的用法。

需要注意的是:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的----vue官方文档。所以,上面 A.vue 的 name 如果改变了,B.vue 的 this.name 是不会改变的,仍然是 科大讯飞。

3.provide与inject 怎么实现数据响应式

一般来说,有两种办法:

provide祖先组件的实例,然后在子孙组件中注入依赖,这样就可以在子孙组件中直接修改祖先组件的实例的属性,不过这种方法有个缺点就是这个实例上挂载很多没有必要的东西比如props,methods

使用2.6最新API Vue.observable 优化响应式 provide(推荐) 我们来看个例子:孙组件E 和 获取A组件传递过来的color值,并能实现数据响应式变化,即A组件的color变化后,组件E、F会跟着变(核心代码如下:)

 

 

// A 组件 
<div>
      <h1>A 组件</h1>
      <button @click="() => changeColor()">改变color</button>
      <ChildrenB />
      <ChildrenC />
</div>
......
  data() {
    return {
      color: "blue" }; }, // provide() { // return { // theme: { // color: this.color //这种方式绑定的数据并不是可响应的 // } // 即A组件的color变化后,组件E、F 不会跟着变 // }; // }, provide() { return { theme: this//方法一:提供祖先组件的实例 }; }, methods: { changeColor(color) { if (color) { this.color = color; } else { this.color = this.color === "blue" ? "red" : "blue"; } } } // 方法二:使用vue2.6最新API Vue.observable 优化响应式 provide // provide() { // this.theme = Vue.observable({ // color: "blue" // }); // return { // theme: this.theme // }; // }, // methods: { // changeColor(color) { // if (color) { // this.theme.color = color; // } else { // this.theme.color = this.theme.color === "blue" ? "red" : "blue"; // } // } // } 复制代码
// F 组件 
<template functional>
  <div class="border2">
    <h3 :style="{ color: injections.theme.color }">F 组件</h3>
  </div>
</template>
<script>
export default {
  inject: {
    theme: {
      //函数式组件取值不一样
      default: () => ({})
    }
  }
};
</script>
复制代码

虽说provide 和 inject 主要为高阶插件/组件库提供用例,但如果你能在业务中熟练运用,可以达到事半功倍的效果!

方法六、 parent / children & ref

$parent / $children:访问父 / 子实例
ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
复制代码

需要注意的是:这两种都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据。我们先来看个用 ref来访问组件的例子:

// component-a 子组件
export default {
  data () {
    return { title: 'Vue.js' } }, methods: { sayHello () { window.alert('Hello'); } } } 复制代码
// 父组件
<template>
  <component-a ref="comA"></component-a>
</template>
<script>
  export default {
    mounted () { const comA = this.$refs.comA; console.log(comA.title); // Vue.js comA.sayHello(); // 弹窗 } } </script> 复制代码
$parent 和 $children
复制代码

这里要说的这种方式就比较直观了,直接操作父子组件的实例。parent 就是父组件的实例对象,而children 就是当前实例的直接子组件实例了,不过这个属性值是数组类型的,且并不保证顺序,也不是响应式的。 parent组件:

<template>
    <div class="">
      <p>this is parent component!</p>
        <button @click="changeChildValue">test</button>
      <child />
    </div>
</template>

<script>
export default {
    data() { return { message: 'hello' } }, methods: { changeChildValue(){ this.$children[0].mymessage = 'hello'; } }, } </script> <style lang="less" scoped> </style> 复制代码

children组件:

<template>
    <div class="">
      <input type="text" v-model="mymessage" @change="changeValue" /> </div> </template> <script> export default { data() { return { mymessage: this.$parent.message } }, methods: { changeValue(){ this.$parent.message = this.mymessage;//通过如此调用可以改变父组件的值 console.log(this.$parent.message) } } } </script> <style lang="less" scoped> </style> 复制代码

不过,这两种方法的弊端是,无法在跨级或兄弟间通信。

// parent.vue
<component-a></component-a>
<component-b></component-b>
<component-b></component-b>
复制代码

我们想在 component-a 中,访问到引用它的页面中(这里就是 parent.vue)的两个 component-b 组件,那这种情况下,就得配置额外的插件或工具了,比如 Vuex 和 Bus 的解决方案。

总结

常见使用场景可以分为三类:
父子通信: 父向子传递数据是通过 props,子向父是通过 events( $emit);通过父链 / 子链也可以通信( $parent / $children);ref 也可以访问组件实例;provide / inject API; $attrs/$listeners 兄弟通信: Bus;Vuex 跨级通信: Bus;Vuex;provide / inject API、 $attrs/$listeners 

 

Guess you like

Origin www.cnblogs.com/baoshuyan66/p/11269756.html