Vue组件通信

Vue组件通信

概念引入

Vue组件通信

 

组件是Vue的一个重要概念,有效的利用了封装的思想(可以类比js的函数或者类)。我们通常会把一个单页应用中的各种模块拆分成一个一个单独的组件,利用这些组件组装成一个完整的功能,具有很好的复用性和维护性。

既然是封装就涉及输入和输出,而多个组件之间如何处理输入和输出是我们经常会面临到的问题,也是我们经常说的组件通信,通信的本质是需要通信的两个组件可以说上话,所以以下我们针对这个问题进行一些总结。

组件间通信

1、父子组件通信:props/$emit

父组件A使用props向子组件B传递消息,比如父组件中使用<Child title="my name is Parent" ></Child>可以向子组件传递title,子组件B使用 props: ["title"]接收后即可使用(当然props接收的格式有很多详情可以查阅https://cn.vuejs.org/v2/guide/components-props.html

子组件B通过$emit向父组件A发送消息,父组件A通过v-on或者@获取传递的信息,比如子组件B通过this.$emit("child", "123");

发送了123,A组件通过@监听child <Child title="my name is Parent" @child="handleChild"></Child> 即可捕获B发出的123。

//Child.Vue
​
<template>
  <div>
    <h1>
      {{ title }}
      <button @click="handleClick">emit</button>
    </h1>
    child
  </div>
</template>
​
<script>
export default {
  props: ["title"],
​
  methods: {
    handleClick() {
      this.$emit("child", "123");
    },
  },
};
</script>
​
<style scoped></style>
​
//Parent.vue
​
<template>
  <div>
    Parent
​
    <CompA>
      <Child title="my name is Parent" @child="handleChild"></Child>
    </CompA>
  </div>
</template>
​
<script>
import Child from "./Child";
export default {
  components: {
    Child
  },
  methods: {
    handleChild(msg) {
      console.log(msg);
    },
  },
};
</script>
​
<style scoped></style>
​
//Child.Vue
<template>
  <div>
    <h1>
      {{ title }}
      <button @click="handleClick">emit</button>
    </h1>
    child
  </div>
</template>
​
<script>
export default {
  props: ["title"],
​
  methods: {
    getTitle() {
      return this.title;
    },
    handleClick() {
      //   this.$emit("child", "123");
      console.log(this.$parent);
    },
  },
};
</script>
​
<style scoped></style>
​
//Parent.vue
<template>
  <div>
    Parent
​
    <CompA>
      <Child title="my name is Parent" ref="compA" @child="handleChild"></Child>
    </CompA>
    <input type="text" ref="input" />
  </div>
</template>
​
<script>
import Child from "./Child";
import CompA from "./CompA";
export default {
  components: {
    Child,
    CompA
  },
​
  mounted() {
    // Parent -> child
    // console.log(this.$children);
    this.$children[0].title = "pass children set value";
    console.log(this.$children[0]);
    const child = this.$children[0];
    console.log(child.getTitle());
    // console.log(this.$refs.compA.getTitle());
    // this.$refs.input.focus();
  },
  methods: {
    handleChild(msg) {
      console.log(msg);
    },
  },
};
</script>
​
<style scoped></style>
​
​
<template>
    <div>
        compA
    </div>
</template>
​
<script>
    export default {
        
    }
</script>
​
<style scoped>
​
</style>
provide() {
    return {
      foo: "bar",
      compA: this,
    };
  },
//CompA
<template>
  <div>
    CompA
    <CompB title="my name is CompA" v-bind="$attrs" v-on="$listeners"></CompB>
  </div>
</template>
​
<script>
import CompB from "./CompB";
export default {
  //   props: ["title"],
  inheritAttrs: false,
  provide() {
    return {
      foo: "bar",
      compA: this,
    };
  },
  components: {
    CompB,
  },
  mounted() {
    // attrs -> 如果你没有在 props 里面定义的参数,都会到这里来
    console.log(this.$attrs);
    console.log(this)
  },
  methods: {
    getTitle() {
      return "heiheihei";
    },
  },
};
</script>
​
<style scoped></style>
​
//CompB
<template>
  <div>
    CompB
    <CompC v-bind="$attrs"></CompC>
  </div>
</template>
​
<script>
import CompC from "./CompC";
export default {
  inject: ["foo", "compA"],
  //   props: ["title"],
  components: {
    CompC,
  },
  mounted() {
    console.log(this.$attrs);
    console.log(this.foo);
    console.log(this.$listeners)
  },
};
</script>
​
<style scoped></style>
​

详细材料详见https://vuex.vuejs.org/zh/

Vue组件通信

 

  • state 存放状态

  • mutations state成员操作

  • getters 加工state成员给外界

  • actions 异步操作

  • modules 模块化状态管理

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式,可以帮助我们管理共享状态,并附带了更多的概念和框架,中大型项目考虑使用,小型项目其实有些冗余。成员如下:

7、vuex

//CompC
<template>
  <div>
    CompC
    <button @click="handleClick">click</button>
  </div>
</template>

<script>
import { EventBus } from "../eventBus";
export default {
  mounted() {},
  methods: {
    handleClick() {
      EventBus.$emit("child", "我是组件c");
    },
  },
};
</script>

<style scoped></style>
//CompB
<template>
  <div>
    CompB
    <CompC></CompC>
  </div>
</template>

<script>
import CompC from "./CompC";
import { EventBus } from "../eventBus";
export default {
  components: {
    CompC,
  },
  mounted() {
    // 侦听
    EventBus.$once("child", (msg) => {
      console.log(msg);
    });
  },
};
</script>

<style scoped></style>
//CompA
<template>
  <div>
    CompA
    <CompB></CompB>
  </div>
</template>

<script>
import CompB from "./CompB";
import { EventBus } from "../eventBus";
export default {
  components: {
    CompB,
  },
  mounted() {
    // 侦听
    // EventBus.$on("child", (msg) => {
    //   console.log(msg);
    // });
    EventBus.$on("child", this.handleChild);
  },
  methods: {
    handleChild() {
      console.log("handleChild");
      // 移除
      EventBus.$off("child", this.handleChild);
    },
  },
};
</script>

<style scoped></style>
//eventBus.js
import Vue from "vue";

export const EventBus = new Vue();

代码块如下:

详情可以查阅https://cn.vuejs.org/v2/guide/components-edge-cases.html#%E7%A8%8B%E5%BA%8F%E5%8C%96%E7%9A%84%E4%BA%8B%E4%BB%B6%E4%BE%A6%E5%90%AC%E5%99%A8

通过 $emit(eventName, eventHandler)` 发送一个事件

通过$off(eventName, eventHandler)` 停止侦听一个事件

通过$once(eventName, eventHandler)` 一次性侦听一个事件

通过 $on(eventName, eventHandler)` 侦听一个事件

6、非关系组件通信EventBus

//CompC
<template>
  <div>
    CompC
    ------
    attrs
    {{ $attrs.title }}
    <div>
      {{ foo }}
    </div>
  </div>
</template>

<script>
export default {
  inject: ["foo", "compA"],
  // props: ["title"],

  mounted() {
    // console.log(this.compA);
    // const parentTitle = this.compA.getTitle();
    // console.log(parentTitle);
    console.log(this.$attrs);
  },
};
</script>

<style scoped></style>

以上两种的代码实例如下:

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

$attrs包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外),当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。

搭配 inheritAttrs: false 使用

5、多层级父子组件通信:$attrs/$listeners

provide / inject API主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了- -种主动提供与依赖注入的关系。

子组件 inject: ["foo", "compA"]。

父组件(只能通过函数返回对象,类似data)

provide提供给子组件要使用的数据,可以在子组件中注入依赖,这样子组件就可以直接修改祖先组件实例的属性,比如

这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。

4、多层级父子组件通信:provide/inject

以上两种弊端是无法跨级或者兄弟间通信,代码块如下:

$children获取当前组件的子组件实例

$parent获取当前组件的父组件实例

3、父子组件通信:$parent/$children

$refs获取通过 ref 注册的引用。

ref给元素或子组件注册引用信息,引用信息将会注册在父组件的 $refs 对象上,如果是在普通的DOM元素上使用,引用指向的就是 DOM 元素,如果是在子组件上,引用就指向组件的实例。比如<Child title="my name is Parent" ref="compA" @child="handleChild"></Child>,

2、父子组件通信:$refs/ref