Inter-component communication vue six ways (full version)

Inter-component communication vue six ways (full version)

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:

image

 

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? This is what we want to explore the topic. This paper summarizes the components between vue several ways to communicate, such as props, $emit/ $on, vuex, $parent /  $children, $attrs/ $listeners, and provide / inject, to tell the difference straightaway examples of which and usage scenarios, we want to help small partners slightly.

Code for this article, please Mengchuo github blog , on paper come Zhongjue, everyone to get knocked codes and more!

Method one, props/$emit

A parent component by way of props to transfer subassembly B, B to A $ emit, A v-on component of the B component in the manner adopted.

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: How to obtain the data in the parent components App.vue subassembly in Users.vue users:["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.

 

Before parent component subassembly to pass 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 v-on: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.

The second method $emit/$on

This method Vue examples of an empty central bus event (Event Center), and use it to listen for events triggering event, ingenious and light quantity implement communications between any of the components, including parent-child, sibling, cross-level. When our project is relatively large, you can choose a better state management solutions vuex.

1. Specific ways:

    var Event=new Vue();
    Event.$emit(事件名,数据);
    Event.$on(事件名,data => {});
复制代码

2. For example,

Suppose there are three brothers components, namely A, how B, C component, C component data acquisition component A or B

<div id="itany">
	<my-a></my-a>
	<my-b></my-b>
	<my-c></my-c>
</div>
<template id="a">
  <div>
    <h3>A组件:{{name}}</h3>
    <button @click="send">将数据发送给C组件</button> </div> </template> <template id="b"> <div> <h3>B组件:{{age}}</h3> <button @click="send">将数组发送给C组件</button> </div> </template> <template id="c"> <div> <h3>C组件:{{name}},{{age}}</h3> </div> </template> <script> var Event = new Vue();//定义一个空的Vue实例 var A = { template: '#a', data() { return { name: 'tom' } }, methods: { send() { Event.$emit('data-a', this.name); } } } var B = { template: '#b', data() { return { age: 20 } }, methods: { send() { Event.$emit('data-b', this.age); } } } var C = { template: '#c', data() { return { name: '', age: "" } }, mounted() {//在模板编译完成后执行 Event.$on('data-a',name => { this.name = name;//箭头函数内部不会产生新的this,这边如果不用=>,this指代Event }) Event.$on('data-b',age => { this.age = age; }) } } var vm = new Vue({ el: '#itany', components: { 'my-a': A, 'my-b': B, 'my-c': C } }); </script> 复制代码

 

image $on  Monitor the data-a custom event and data-b, because sometimes uncertain when it will trigger the event, usually to listen to or created in the mounted hooks.

 

Method three, vuex

 

image

 

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 assembly by $store.dispatch('action 名称', data1)triggered. 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: changing the operating state of the method, by the actions of commit('mutation 名称')a trigger. Is the only recommended method Vuex modify the state. This method can only operate synchronously, and the method name can only be globally unique. Among the operation there will be some hook exposed to monitor the state and so on.
  • state: Page Status Management container object. Vue components centrally stored in data objects scattered data, globally unique, unified state management. Page required to display data read from the subject, using the fine-grained data Vue response mechanism to perform efficient update.
  • getters: state method for reading objects. FIG not separately listed in the module, should be included in the render in, Vue Components read global target state by this method.

3.Vuex and localStorage

vuex is vue state manager, data storage is the type of response. But does not save them, then refresh returned to the initial state, which would be in time vuex data changes in the data localStorage to save a copy of which, after refreshing, if there localStorage to save the data, taken out and then replaced store in the state.

let defaultCity = "上海"
try {   // 用户关闭了本地存储功能,此时在外层加个try...catch
  if (!defaultCity){
    defaultCity = JSON.parse(window.localStorage.getItem('defaultCity'))
  }
}catch(e){}
export default new Vuex.Store({ state: { city: defaultCity }, mutations: { changeCity(state, city) { state.city = city try { window.localStorage.setItem('defaultCity', JSON.stringify(state.city)); // 数据改变的时候把数据拷贝一份保存到localStorage里面 } catch (e) {} } } }) 复制代码

It should be noted that: due vuex, we saved state, is an array, but localStorage only supports strings, so the need to use JSON conversion:

JSON.stringify(state.subscribeList);   // array -> string
JSON.parse(window.localStorage.getItem("subscribeList"));    // string -> array 
复制代码

Methods IV $attrs/$listeners

1 Introduction

Multistage nesting assembly need to pass data, a method generally used by vuex. However, if only data transmission, without making an intermediate processing, use vuex process, a bit overkill. Aims to provide a Vue2.4 version Another method ---- $attrs/$listeners

  • $attrs: Contains the parent scope prop not recognized (and obtain) the binding properties (except for class and style). When a component does not declare any prop, where the parent scope includes all bindings (other than class style), and can be v-bind = "$ attrs" Incoming internal components. Usually used in conjunction with inheritAttrs option.

  • $listeners: It contains the parent scope (excluding .native decorator) v-on event listener. It can be v-on = "$ listeners" Incoming internal components

Next we look at an example of cross-level communication:

// index.vue
<template>
  <div>
    <h2>浪里行舟</h2>
    <child-com1
      :foo="foo"
      :boo="boo"
      :coo="coo"
      :doo="doo"
      title="前端工匠" ></child-com1> </div> </template> <script> const childCom1 = () => import("./childCom1.vue"); export default { components: { childCom1 }, data() { return { foo: "Javascript", boo: "Html", coo: "CSS", doo: "Vue" }; } }; </script> 复制代码
// childCom1.vue
<template class="border">
  <div>
    <p>foo: {{ foo }}</p>
    <p>childCom1的$attrs: {{ $attrs }}</p>
    <child-com2 v-bind="$attrs"></child-com2> </div> </template> <script> const childCom2 = () => import("./childCom2.vue"); export default { components: { childCom2 }, inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性 props: { foo: String // foo作为props属性绑定 }, created() { console.log(this.$attrs); // { "boo": "Html", "coo": "CSS", "doo": "Vue", "title": "前端工匠" } } }; </script> 复制代码
// childCom2.vue
<template>
  <div class="border">
    <p>boo: {{ boo }}</p>
    <p>childCom2: {{ $attrs }}</p>
    <child-com3 v-bind="$attrs"></child-com3>
  </div>
</template>
<script>
const childCom3 = () => import("./childCom3.vue"); export default { components: { childCom3 }, inheritAttrs: false, props: { boo: String }, created() { console.log(this.$attrs); // { "coo": "CSS", "doo": "Vue", "title": "前端工匠" } } }; </script> 复制代码
// childCom3.vue
<template>
  <div class="border">
    <p>childCom3: {{ $attrs }}</p>
  </div>
</template>
<script>
export default {
  props: {
    coo: String,
    title: String
  }
};
</script>
复制代码

 

image As shown above $attrs represents not inherit the data object, format {attribute name: attribute value}. Vue2.4 provides $attrs  ,  $listeners  to pass data and events, cross-level communication between the components easier.

 

In short: $attrsthe $listeners two objects, $attrs where the parent component is stored in a non-binding Props attributes $listenersin storage is non-native binding in the event the parent component.

Method five, provide / inject

1 Introduction

Vue2.2.0 new API, this option needs to be used together to allow an ancestor component inject a dependency to all future generations, regardless of how deep the component level, and in the upstream and downstream relations since the establishment of the time is always effective. A word which encapsulates: ancestral components provided by the variable provider, and then injected through the assembly offspring variables inject. provide / inject API mainly to solve the communication problem between the cross-level components, but its usage scenarios, mainly sub-assembly components to obtain higher status, between the cross-level components to establish a relationship of dependency injection and unsolicited.

2. For example,

Suppose there are two components: A.vue and B.vue, B is a sub-assembly 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(推荐)

我们来看个例子:孙组件D、E和F获取A组件传递过来的color值,并能实现数据响应式变化,即A组件的color变化后,组件D、E、F不会跟着变(核心代码如下:)

 

image

 

// 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变化后,组件D、E、F不会跟着变
  //   };
  // },
  provide() {
    return {
      theme: this//方法一:提供祖先组件的实例
    };
  },
  methods: {
    changeColor(color) {
      if (color) {
        this.color = color;
      } else {
        this.color = this.color === "blue" ? "red" : "blue";
      }
    }
  }
  // 方法二:使用2.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

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

Note that: both of which are directly component instance, methods or data access components can be called directly after use. Let's look at a used  refexample to access components:

// 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> 复制代码

However, the drawbacks of these two methods is unable to communicate among cross-level or brothers.

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

We would like component-a, a reference to its access to the page (this is parent.vue) of the two component-b component that in this case, you have to configure additional plug-in or a tool, such as Vuex and Bus s solution.

to sum up

Common usage scenarios can be divided into three categories:

  • Communication Sons: transmitted to the parent sub-data by props, by the child to the parent Events ( $emit); by the parent chain / daughter strand may also communicate ( $parent /  $children); REF can also access the component instance; provide / inject API;$attrs/$listeners
  • Brothers Communications: Bus; Vuex
  • Cross-level communication: Bus; Vuex; provide / inject API,$attrs/$listeners

Guess you like

Origin www.cnblogs.com/shaozhu520/p/10926647.html