vue 组件通信方式

  1. 父组件将自己的状态分享给子组件使用;

    方法:父组件通过子标签传递数据,子组件通过 props 接收

  2. 子组件改变父组件的状态;

    方法:父组件在子标签上通过@abc 提供一个改变自身状态的方法,子组件通过$emit(“abc”, payload)触发这个函数

  3. 父组件直接改变子组件的状态;

    方法:父组件设法($ref,$children[0])拿到子组件实例对象,然后通过实例对象直接修改子组件状态

  4. 子组件直接改变父组件的状态

    方法:子组件通过$parent拿到父组件的改变自身状态的方法,然后直接调用($parent 可以拿到父组件状态,但是最好不要直接修改,而是通过父组件函数式修改,保持单向数据流)

  5. 父组件通过插槽向子组件传递数据

    方法:子组件定义具名插槽,父组件向插槽内传递自己的状态

  6. 父组件向后代组件传值

    方法:父组件正常在标签上向子组件传递数据,子组件不用 props 接收属性,通过$attrs获取属性,通过$listeners 获取方法。子组件再向下传递时,使用 v-bind=”$attrs”传递属性,使用v-on=”$listeners”传递方法

  7. 父组件向后代组件传值

    方法:父组件js中定义provide函数提供数据源,后代组件通过inject去接收,inject可以是一个数组或对象。

    注意:父组件提供的数据源如果不是响应式的,那么后代修改数据,数据不会响应变化。如果父组件提供的数据源是响应式的,但是不是一个对象,那么它也不是响应式的,所以要用对象在外包一层对象(数组不行)。另外,如果子组件同时provide一个inject祖先组件相同名称的数据,那么子组件的后代会就近使用子组件的数据。

    官网tip:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。

    https://cn.vuejs.org/v2/api/#provide-inject

  8. 全局通信-事件总线

    方法:通过注册一个新的vue实例作为桥梁,使用$on和$emit来完成通信

  9. 全局通信-vuex

    略(看官方文档喽)

    https://vuex.vuejs.org/zh/

第一种: props传参

// 父组件向子组件传递msg
<template>
  <div>
    <p>我是dad</p>
    <Child :msg="msg" />
  </div>
</template>

<script>
import Child from "./ChildItem.vue";

export default {
  name: "App",
  components: {
    Child,
  },
  data() {
    return {
      msg: "父亲的忠告",
    };
  },
};
</script>

// 子组件props接收
<template>
  <div>
    <p>我是子组件</p>
    <span>{{ msg }}</span>
  </div>
</template>

<script>
export default {
  props: {
    msg: {
      type: String,
      default: "什么都没有",
    },
  },
};
</script>


第二种:emit,on通信

// 父组件向子组件提供改变自己状态的函数
<template>
  <div>
    <p>我是dad</p>
    <Child @changeMyMind="changeMyMind" />
    <span>{{ mind }}</span>
  </div>
</template>

<script>
import Child from "./ChildItem.vue";

export default {
  name: "App",
  components: {
    Child,
  },
  data() {
    return {
      mind: "伟大万岁",
    };
  },
  methods: {
    changeMyMind(yourMind) {
      this.mind = yourMind;
    },
  },
};
</script>


// 子组件不用接收,直接通过$emit触发并传参就行
<template>
  <div>
    <p>我是子组件</p>
  </div>
</template>

<script>
export default {
  mounted() {
    this.$emit("changeMyMind", "躺平鸟");
  },
};
</script>

第三种:$ref,$children实例通信

// 父组件通过$ref或者$children拿到子组件实例,然后直接修改子组件状态
/**
 * this.$children`数组中的元素不是响应式的,并且下标并不一定对用父组件引用的子组件的顺序,例如有异步加载的子组件,可能影响其在 children 数组中的顺序。所以使用时需要根据一定的条件例如子组件的name去找到相应的子组件。
 **/
<template>
  <div>
    <p>我是dad</p>
    <Child ref="childRef" />
  </div>
</template>

<script>
import Child from "./ChildItem.vue";

export default {
  components: {
    Child,
  },
  mounted() {
    // this.$children[0].childMsg = "不你不是";
    this.$refs.childRef.childMsg = "不你不是";
  },
};
</script>



// 子组件等着被改就行
<template>
  <div>
    <p>我是子组件</p>
    <span>{{ childMsg }}</span>
  </div>
</template>

<script>
export default {
  data() {
    return {
      childMsg: "我是子组件数据",
    };
  },
};
</script>


第四种:$parent通信

// 父组件
<template>
  <div>
    <p>我是dad</p>
    <Child />
    <span>{{ aa }}</span>
  </div>
</template>

<script>
import Child from "./ChildItem.vue";

export default {
  components: {
    Child,
  },
  data() {
    return {
      aa: "",
    };
  },
  methods: {
    changeAa(data) {
      this.aa = data;
    },
  },
};
</script>

// 子组件通过$parent拿到父组件实例,然后直接修改父组件状态
<template>
  <div>
    <p>我是子组件</p>
    <span>{{ childMsg }}</span>
  </div>
</template>

<script>
export default {
  data() {
    return {
      childMsg: "我是子组件数据",
    };
  },
  mounted() {
    // this.$parent.aa = "我改了哈"; 不推荐
    this.$parent.changeAa("我改了哦");
  },
};
</script>

第五种:插槽通信(一般不用)

// 父组件
<template>
  <div>
    <p>我是dad</p>
    <Child>
      <template v-slot:boring>
        {{ aa }}
      </template>
    </Child>
  </div>
</template>

<script>
import Child from "./ChildItem.vue";

export default {
  components: {
    Child,
  },
  data() {
    return {
      aa: "父组件的数据哦",
    };
  },
};
</script>


// 子组件定义插槽
<template>
  <div>
    <p>我是子组件</p>
    <slot name="boring"></slot>
  </div>
</template>

<script>
export default {};
</script>


第六种:$attrs,$listeners深层双向通信

// 父组件
<template>
  <div>
    <p>我是dad</p>
    <span>{{ dadData }}</span>
    <Son :dadData="dadData" @changeDadData="changeDadData" @keyup="someKeyUp" />
  </div>
</template>

<script>
import Son from "./SonItem.vue";

export default {
  components: {
    Son,
  },
  data() {
    return {
      dadData: "父组件的数据哦",
    };
  },
  methods: {
    changeDadData(newData) {
      this.dadData = newData;
    },
    someKeyUp(e) {
      console.log(e.target.value);
    },
  },
};
</script>



// 儿子组件
<template>
  <div>
    <p>我是儿子组件</p>
    <span>{{ $attrs.dadData }}</span>
    <input type="text" v-on="$listeners" />
    <GrandSon v-bind="$attrs" v-on="$listeners" />
  </div>
</template>

<script>
import GrandSon from "./GrandSon.vue";

export default {
  components: {
    GrandSon,
  },
  mounted() {
    console.log(this.$listeners);
  },
};
</script>

// 孙子组件
<template>
  <div>
    <p>我是孙子组件</p>
    <input type="text" @input="grandsonInput" />
  </div>
</template>

<script>
export default {
  methods: {
    grandsonInput(e) {
      //   this.$emit("changeDadData", e.target.value); 也可以触发
      this.$listeners.changeDadData(e.target.value);
    },
  },
};
</script>



第七种:provide,inject依赖注入深层次单向通信

// 父组件
<template>
  <div>
    <p>我是dad</p>
    <span>{{ dadMessage }}</span>
    <Son />
  </div>
</template>

<script>
import Son from "./SonItem.vue";

export default {
  components: {
    Son,
  },
  provide() {
    return {
      message: this.dadMessage,
    };
  },
  data() {
    return {
      dadMessage: {
        textContent: "我是个祖先数据",
      },
    };
  },
};
</script>


// 儿子组件
<template>
  <div>
    <p>我是儿子组件</p>
    <span>{{ theData }}</span>
    <GrandSon />
  </div>
</template>

<script>
import GrandSon from "./GrandSon.vue";

export default {
  components: {
    GrandSon,
  },
  inject: {
    // 起个别名
    theData: {
      // 数据来源映射
      from: "message",
      // 默认值
      default: () => ({ message: { textContent: "啥也不是" } }),
    },
  },
};
</script>


// 孙子组件
<template>
  <div>
    <p>我是孙子组件</p>
    <input type="text" @input="grandsonInput" />
    <span>{{ message.textContent }}</span>
  </div>
</template>

<script>
export default {
  methods: {
    grandsonInput(e) {
      this.message.textContent = e.target.value;
    },
  },
  inject: ["message"],
};
</script>



第八种: $bus事件总线全局通信

// main.js中定义新的vue实例挂载到原型上
Vue.prototype.$bus = new Vue();

// 父组件通过this.$bus.$on监听事件
<template>
  <div>
    <p>我是dad</p>
    <span>{{ dadData }}</span>
    <Son />
  </div>
</template>

<script>
import Son from "./SonItem.vue";

export default {
  components: {
    Son,
  },
  data() {
    return {
      dadData: "我是爹地",
    };
  },
  mounted() {
    this.$bus.$on("changeDadData", this.changeDadData);
  },
  methods: {
    changeDadData(newData) {
      this.dadData = newData;
    },
  },
  // 记得清除监听
  beforeDestroy() {
    this.$bus.$off("changeDadData");
  },
};
</script>


// 孙子组件通过this.$bus.$emit触发事件
<template>
  <div>
    <p>我是孙子组件</p>
    <input type="text" @input="grandsonInput" />
    <span></span>
  </div>
</template>

<script>
export default {
  methods: {
    grandsonInput(e) {
      this.$bus.$emit("changeDadData", e.target.value);
    },
  },
};
</script>