Communication between components in Vue - six ways - summary

Method 1: props/$emit

Pass value from parent component to child component

Use an example to illustrate how the parent component passes values ​​to the child component: How to get the data in the parent component App.vue in the child component Users.vue users:["Henry","Bucky","Emily"]

Note: The parent component passes data down to the child component through props. Note: There are three forms of data in components: data, props, computed

//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>

Child component passes value to parent component (event form)

Let’s use an example to illustrate how the child component passes the value to the parent component: when we click on "Vue. Component pass value" to realize the transfer of values ​​from child components to parent components

Note: The child component sends messages to the parent component through events, in fact, the child component sends its own data to the parent component

// 子组件
<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>

 Method 2: eventBus ( $emit/ $on)

Use an empty Vue instance as the central event bus (event center), use it to trigger events and listen to events, and cleverly and lightweightly realize the communication between any components, including parent-child, brother, and cross-level. When our project is relatively large, we can choose a better state management solution vuex

How to use

let Event=new Vue();
Event.$emit(事件名,数据);
Event.$on(事件名,data => {});

// 或者在main.js中定义全局使用
Vue.prototype.$bus = new Vue();

There are three components, namely A, B, and C components. How does C component obtain the data of A or B component?

Note: $on The custom events data-a and data-b are listened to, because sometimes it is not sure when the event will be triggered, and it is usually listened in the mounted or created hook


<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>

Method 3: Vuex

 Vue Components : Vue components. On the HTML page, it is responsible for receiving interactive actions such as user operations, and executes the dispatch method to trigger the corresponding action to respond.
dispatch : The trigger method of the operation behavior, which is the only method that can execute the action.
actions : Operation behavior processing module, triggered by $store.dispatch('action name', data1) in the component. Then commit () triggers the mutation call to update the state indirectly. Responsible for handling all interactions received by Vue Components. Contains synchronous/asynchronous operations, supports multiple methods with the same name, and triggers sequentially according to the order of registration. The operations requested to the background API are performed in this module, including triggering other actions and submitting mutations. This module provides the encapsulation of Promise to support chain triggering of actions.
commit : The status change commit operation method. Submitting a mutation is the only way to execute a mutation.
mutations : state change operation method, triggered by commit('mutation name') in actions. is the only recommended way for Vuex to modify state. This method can only perform synchronous operations, and the method name can only be globally unique. During the operation, some hooks will be exposed for state monitoring and so on.
state : The page state management container object. Centrally store the scattered data of data objects in Vue components, which is globally unique for unified state management. The data required for page display is read from this object, and Vue's fine-grained data response mechanism is used to perform efficient state updates.
getters: State object reading method. The module is not listed separately in the figure and should be included in render. Vue Components reads the global state object through this method.

Method 4: $attrs/$listeners

  • When multi-level component nesting needs to pass data, the usual method is through vuex. But if you just pass data without doing intermediate processing, using vuex for processing is a bit overkill. For this Vue2.4 version provides another method ----$attrs/$listeners
  • $attrs: Contains attribute bindings (except class and style) that are not recognized (and acquired) by prop in the parent scope. When a component does not declare any props, all parent scope bindings (except class and style) will be included here, and internal components can be passed in via v-bind="$attrs". Usually used in conjunction with the inheritAttrs option.
  • $listeners: Contains the v-on event listeners in the parent scope (without the .native decorator). It can be passed to internal components via v-on="$listeners"

// 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>

$attrs shown in the figure above means an object without inherited data, and the format is {attribute name: attribute value}. Vue2.4 provides $attrs and $listeners to pass data and events, and the communication between cross-level components becomes easier.
To put it simply: $attrs and $listeners are two objects, $attrs store the non-Props properties bound in the parent component, and $listeners store the non-native events bound in the parent component.

Method 5: provide/inject

Vue2.2.0 has a new API. This pair of options needs to be used together to allow an ancestor component to inject a dependency to all its descendants, no matter how deep the component hierarchy is, and it will always take effect when the upstream and downstream relationships are established. In a nutshell: variables are provided through providers in ancestor components, and then variables are injected through inject in descendants components. The provide / inject API mainly solves the communication problem between cross-level components, but its usage scenario is mainly that sub-components obtain the state of the upper-level components, and a relationship between active provision and dependency injection is established between cross-level components.

// A.vue
export default {
  provide: {
    name: '浪里行舟'
  }
}
-------------------------------------------
// B.vue
export default {
  inject: ['name'],
  mounted () {
    console.log(this.name);  // 浪里行舟
  }
}

It can be seen that in A.vue, we set a provide: name, the value is sailing in the waves, and its function is to provide the variable name to all its subcomponents. In B.vue, the name variable provided from component A is injected through inject, so in component B, this variable can be accessed directly through this.name, and its value is also sailing in the waves. This is the core usage of provide / inject API.

Note: provide and inject bindings are not responsive. This is deliberate. However, if you pass in an object that can be listened to, the properties of the object are still responsive ---- vue official document So, if the name of A.vue above changes, the this.name of B.vue is not It will change, but it is still sailing in the waves.

How provide and inject implement data responsiveness

Generally speaking, there are two ways:

Provide an instance of the ancestor component, and then inject dependencies in the descendant component, so that the properties of the instance of the ancestor component can be directly modified in the descendant component, but this method has a disadvantage that many unnecessary things such as props are mounted on this instance , methods
use the latest 2.6 API Vue.observable to optimize the responsive provide (recommended)
Let's look at an example: the grandson components D, E and F obtain the color value passed by the A component, and can realize data responsive changes, that is, the A component After the color changes, components D, E, and F will change accordingly

// 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>

 Although provide and inject mainly provide use cases for high-level plug-in/component libraries, if you can use them proficiently in business, you can achieve twice the result with half the effort

Method 6: $parent /  $childrenwith ref

  • ref: If used on a normal DOM element, the reference points to the DOM element; if used on a child component, the reference points to the component instance
  • $parent /  $children: Access parent/child instances

It should be noted that these two types are to directly obtain the component instance, and after use, you can directly call the method of the component or access the data. Let's first look at an example reffor accessing 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>
————————————————————————————————————————————————————————————————————————————————
// 不过,这两种方法的弊端是,无法在跨级或兄弟间通信
// parent.vue
<component-a></component-a>
<component-b></component-b>
<component-b></component-b>

If you want to access two component-b components in the page that references it (here is parent.vue) in component-a, in this case, you have to configure additional plug-ins or tools, such as Vuex and Bus solution


Parent-child communication :
the parent transmits data to the child through props, and the child to the parent through events ($emit); through the parent chain/child chain can also communicate ($parent / $children); ref can also access component instances; provide / inject API; $attrs/$listeners

Brother communication :
Bus; Vuex

Crossover communication :
Bus; Vuex; provide/inject API, $attrs/$listeners

Guess you like

Origin blog.csdn.net/JackieDYH/article/details/124571199