Vue组件通信

一、props / $emit

  1. 父组件向子组件传值
    父组件通过 props 的方式向子组件传递数据,子组件通过 $emit 向父组件通信。
    总结:
    prop 只可以从上一级组件传递到下一级组件(父子组件),即所谓的单向数据流。而且 props 只读,不可被修改,所有修改都会失效并警告。
    如果想要让props成为可修改的值,可以在子组件的data或computed中定义,使props初始化为本地数据
// ParentComponent.vue文件
<template>
  <div>
    <p>我是父组件</p>
    <ChildComponent :showList="showList"></ChildComponent>
  </div>
</template>

<script>
import ChildComponent from "./ChildComponent.vue";
export default {
  components: {
    ChildComponent,
  },
  data() {
    return {
      showList: ["第一行", "第二行", "第三行"],
    };
  },
};
</script>
// ChildComponent.vue文件
<template>
  <div>
    <p>我是子组件</p>
    <ul>
      <li v-for="(item, index) in showList" :key="index">{{ item }}</li>
    </ul>
    <button @click="change">点击改变props数据</button>
  </div>
</template>

<script>
export default {
  props: {
    showList: {
      type: Array, // 规定prop的数据类型
      required: true, // 数据是否为必须
      default: () => [], // 对象或数组默认值必须从一个工厂函数获取
      validator(value) {
        return value.length === 3; // 自定义验证器要求数组长度为3
      },
    },
  },
  methods: {
    change() {
      // this.showList.splice(0, 1, "???"); // 不能在子组件中修改props,会报错
      // this.mutableList.splice(0, 1, "???");
    },
  },
  data() {
    return {
      // mutableList: this.showList,
    };
  },
  computed: {
    // mutableList() {
    //   return this.showList;
    // },
  },
};
</script>
  1. 子组件向父组件传值
    在父组件模板的子组件标签上,用v-on绑定一个在父组件上定义的函数(自定义事件),在子组件上调用实例上的$emit方法,触发自定义事件并传入参数。这个传入的参数就是子组件向父组件发送的数据。
// ParentComponent.vue文件
<template>
  <div>
    <p>{{ title }}</p>
    <ChildComponent v-on:updateParent="updateParent"></ChildComponent>
  </div>
</template>

<script>
import ChildComponent from "./ChildComponent.vue";
export default {
  components: {
    ChildComponent,
  },
  data() {
    return {
      title: "我是父组件",
    };
  },
  methods: {
    updateParent(newTitle) {
      this.title = newTitle;
    },
  },
};
</script>
// ChildComponent.vue文件
<template>
  <div>
    <p>我是子组件</p>
    <button @click="update">更新父组件的标题</button>
  </div>
</template>

<script>
export default {
  methods: {
    update() {
      this.$emit("updateParent", "我真的是父组件");
    },
  },
};
</script>

二、 $children / $parent

通过 $parent 和 $children 就可以访问组件实例,拿到实例代表什么?代表可以访问此组件的所有方法和data。
要注意边界情况,如在App组件上拿$parent得到的是new Vue()的实例,在根实例上再拿$parent 得到的是undefined,而在最底层的子组件拿$children 是个空数组。
也要注意得到$parent和$children的值不一样, $children 是数组,而$parent是对象;因为子组件可以有多个,而父组件只有一个。

总结
props/$emit和$parent/$children都可以实现父子组件的通信,而不能在非父子组件通信中使用,props最常用。

三、provide/ inject

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

// ParentComponent.vue文件
<template>
  <div>
    <p>{{ title }}</p>
    <ChildComponent></ChildComponent>
  </div>
</template>

<script>
import ChildComponent from "./ChildComponent.vue";
export default {
  components: {
    ChildComponent,
  },
  data() {
    return {
      title: "我是父组件",
    };
  },
  provide: {
    foo: "父组件的数据",
  },
};
</script>
// ChildComponent.vue文件
<template>
  <div>
    <p>我是子组件</p>
    <p>{{ foo }}</p>
    <GrandsonComponent></GrandsonComponent>
  </div>
</template>

<script>
import GrandsonComponent from "./GrandsonComponent";
export default {
  components: {
    GrandsonComponent,
  },
  inject: ["foo"],
};
</script>
// GrandsonComponent.vue文件
<template>
  <div>
    <p>我是孙子组件</p>
    <p>{{ foo }}</p>
  </div>
</template>

<script>
export default {
  inject: ["foo"],
};
</script>

四、ref / refs

ref:如果在普通的 DOM 元素标签上使用,引用指向的就是 DOM 元素;如果用在子组件标签上,引用就指向组件实例,可以通过实例直接调用组件的方法或访问数据

五、Event Bus/事件总线

Event Bus 又称为事件总线,在vue中可以使用它来作为沟通桥梁的概念, 就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所以组件都可以通知其他组件。
Event Bus 也有不方便之处, 当项目较大,就容易造成难以维护的灾难

  1. 创建新的Vue实例,作为事件总线
// event-bus.js文件
import Vue from "vue";
export default new Vue();
// ParentComponent.vue文件
<template>
  <div>
    <p>{{ title }}</p>
    <ChildOne></ChildOne>
    <ChildTwo></ChildTwo>
  </div>
</template>

<script>
import ChildOne from "./ChildOne.vue";
import ChildTwo from "./ChildTwo.vue";
export default {
  components: {
    ChildOne,
    ChildTwo,
  },
  data() {
    return {
      title: "我是父组件",
    };
  },
};
</script>
  1. 在接收端,向事件总线绑定事件$on
// ChildOne.vue文件
<template>
  <div>
    <p>一号子组件</p>
    <p>计数:{{ count }}</p>
  </div>
</template>

<script>
import EventBus from "../bus/event-bus.js";
export default {
  data() {
    return { count: 0 };
  },
  // 在mounted绑定自定义事件
  mounted() {
    // 绑定自定义事件时,回调函数必须是箭头函数,否则this指向的是事件总线vue实例
    EventBus.$on("updateCount", (value) => {
      this.count += value;
    });
  },
  // 在beforeDestroy解绑自定义事件,或定时器
  beforeDestroy() {
    EventBus.$off("updateCount");
  },
};
</script>
  1. 在发送端,向事件总线触发事件$emit,同时传递参数
// ChildTwo.vue文件
<template>
  <div>
    <p>二号子组件</p>
    <button @click="addOne">+2</button>
  </div>
</template>

<script>
import EventBus from "../bus/event-bus.js";
export default {
  methods: {
    addOne() {
      EventBus.$emit("updateCount", 2);
    },
  },
};
</script>
  1. 移除事件监听$off

六、Vuex

  1. Vuex介绍
    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化.
    Vuex 解决了多个视图依赖于同一状态和来自不同视图的行为需要变更同一状态的问题,将开发者的精力聚焦于数据的更新而不是数据在组件之间的传递上
  2. Vuex组成
    state:用于数据的存储,是store中的唯一数据源
    getters:如vue中的计算属性一样,基于state数据的二次包装,常用于数据的筛选和多个数据的相关性计算
    mutations:类似函数,改变state数据的唯一途径,且不能用于处理异步事件
    actions:类似于mutation,用于提交mutation来改变状态,而不直接变更状态,可以包含任意异步操作
    modules:类似于命名空间,用于项目中将各个模块的状态分开定义和操作,便于维护

七、localStorage / sessionStorage

这种通信比较简单,缺点是数据和状态比较混乱,不太容易维护。 通过window.localStorage.getItem(key)获取数据 通过window.localStorage.setItem(key,value)存储数据
注意用JSON.parse() / JSON.stringify() 做数据格式转换 localStorage / sessionStorage可以结合vuex, 实现数据的持久保存,同时使用vuex解决数据和状态混乱问题.

八、$attrs 与 $listeners

可用于隔代通信
inheritAttrs:true(默认)对于绑定的属性,若不是子组件 props 中声明的属性,则出现在子组件的根标签上
inheritAttrs:false 除class、style属性外,绑定的属性都不会出现在根标签上

ParentComponent.vue文件
<template>
  <div>
    <p>{{ title }}</p>
    <ChildComponent :name="name" :age="age" :info="info" @sayHi="sayHi" @change="change"></ChildComponent>
  </div>
</template>

<script>
import ChildComponent from "./ChildComponent.vue";
export default {
  components: {
    ChildComponent,
  },
  data() {
    return {
      title: "我是父组件",
      name: "John",
      age: 18,
      info: {
        sex: "Male",
        address: "German",
      },
    };
  },
  methods: {
    sayHi() {
      console.log("Hello");
    },
    change() {
      this.title = "new title";
    },
  },
};
</script>
ChildComponent.vue文件
<template>
  <div>
    <p>我是子组件</p>
    <!-- 注意在儿子组件中,要在孙子组件标签上,要重新绑定属性,重新绑定自定义事件(简写,不用属性名,直接绑定对象) -->
    <GrandsonComponent v-bind="$attrs" v-on="$listeners"></GrandsonComponent>
  </div>
</template>

<script>
import GrandsonComponent from "./GrandsonComponent";
export default {
  props: ["name"],
  components: {
    GrandsonComponent,
  },
  created() {
    console.log(this.$attrs); // 只有age和info对象,name被props接收了
    console.log(this.$listeners); // 有sayHi和change,父组件绑定过来的自定义事件
  },
  inheritAttrs: false,
};
</script>
GrandsonComponent.vue文件
<template>
  <div>
    <p>我是孙子组件</p>
    <button @click="emitEvent">孙子组件触发父组件向下传递的事件</button>
  </div>
</template>

<script>
export default {
  props: ["age"],
  created() {
    console.log(this.$attrs); // 只有info对象,name被儿子组件的props接收了,age被孙子组件的props接收了
    console.log(this.$listeners); // 有sayHi和change,父组件绑定过来的自定义事件
  },
  methods: {
    emitEvent() {
      this.$emit("sayHi"); // 执行了
      this.$emit("change", "孙子有话要说"); // 执行了,修改了ParentComponent组件(爷爷)的数据
    },
  },
  inheritAttrs: false,
};
</script>

总结

常见使用场景可以分为三类:
父子组件通信: props; $parent / $children; provide / inject ; ref ; $attrs / $listeners
兄弟组件通信: EventBus;Vuex
隔代通信: EventBus;Vuex;provide / inject 、$attrs / $listeners

参考:https://juejin.cn/post/6844903887162310669

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值