听说她还不会组件传值,我连夜为她写了组件传值的七种方式

前言

在抽离公共组件时,会遇到很多需要组件传值的地方,特别是封装对话框组件。回想最初只会props、$emit的我在遭遇组件二次封装时越写越复杂,越写越看不懂,内心悲痛万分。痛心疾首的我熬夜写了这篇文章,帮助你们掌握组件传值的各种花式,玩弄各种业务需求,高强度对线面试官。是的,我心里装的是谁你们都清楚了吧?没有她,只有你们!
在这里插入图片描述

props、$emit

最基本的传值方式,主要用于父子之间,也可以用于爷孙之间(需要父组件做中转)

父子之间

父传子:子组件通过props接收

父组件:
<template>
  <div>
    <Center :cc="c1"></Center>
  </div>
</template>
<script>
import Center from "./center.vue";
export default {
  components: {
    Center,
  },
  data() {
    return {
      c1: "这里是父组件",
    };
  },
};
</script>
子组件:props里的值使用方式同data(){}
<template>
  <div>
    {{ cc }}
  </div>
</template>
<script>
export default {
  props: {
    cc: {
      type: String,
    },
  },
  components: {
    Innner,
  },
  mounted(){
  console.log(this.cc)
  }
};
</script>

在这里插入图片描述
子传父:
子传父通过子模块$emit注册事件,父模块监听事件来获得数据
子组件html:

<button @click="handleChange">改变cc的值</button>

子组件script
这里 onChange即为注册的事件,后面的字符串为传给父组件的值

 handleChange() {
      this.$emit("onChange", "这里是子组件的cc");
    },

父组件html
父组件通过 @onChange来接受子组件传来的值

<Center :cc="c1" @onChange="handle"></Center>

父组件script

   handle(val) {
      this.c1 = val;
    },

结果:
在这里插入图片描述
点击按钮,改变cc的值:
在这里插入图片描述

爷孙传值

爷孙之间的传值通过父组件作为中转,outside将值传给center,center再将值传给inner,反之同理

爷组件outside<Center :aa="a1" ></Center>
父组件center<Innner :aa="aa"></Innner>
props: { aa: { type: String }, cc: { type: String, }, },
子组件innerprops: { aa: { type: String, default: "haha" }, },

孙爷传值

子组件inner

<template>
  <div>
    这里是inner:{{ aa }}
    <button @click="$emit('onInner', '这里是来自inner的值')">按钮</button>
  </div>
</template>
<script>
export default {
  props: {
    aa: { type: String, default: "haha" },
  },
};
</script>

父组件center

<template>
  <div>
    <Innner :aa="aa" @onInner="onCenter"></Innner>
  </div>
</template>
<script>
import Innner from "./innner.vue";
export default {
  props: {
    aa: { type: String },
  },
  components: {
    Innner,
  },
  methods: {
    onCenter(val) {
      this.$emit("onCenter", val);
    },
  },
};
</script>

爷组件outside

<template>
  <div>
    <Center :aa="a1" @onCenter="handleChange"></Center>
  </div>
</template>
<script>
import Center from "./center.vue";
export default {
  components: {
    Center,
  },
  data() {
    return {
      a1: "这里是outside的值",
    };
  },
  methods: {
    handleChange(val) {
      this.a1 = val;
    },
  },
};
</script>

在这里插入图片描述
在这里插入图片描述
爷孙之间传值本质和父子传值相同。同理,只要你会了props,$emit,你就可以实现任意层数的组件嵌套(狗头滑稽)

$ attrs、$listener

$ attrs、$ listener用来二次封装对话框简直不要太香,哈哈。

$ attrs:包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。

这里可以理解为,通过父组件中设置v-bind:’$attrs’,子组件将接受爷组件传的值中父组件没有props接受的值。(建议在父组件center中打印this. $ attrs)
爷组件outside:这里outside传给父组件center,aa、bb、cc三个值

<template>
  <div>
    <Center :aa="a1" :bb="b1" :cc="c1" @onCenter="handleChange"></Center>
  </div>
</template>
<script>
import Center from "./center.vue";
export default {
  components: {
    Center,
  },
  data() {
    return {
      c1: "这里是outside组件的c",
      a1: "这里是outside的a",
      b1: "这里是outside的b",
    };
  },
};
</script>

父组件center:center中props值只接收了cc的值,将aa、bb的值传递给子组件inner。

<template>
  <div>
    <Innner v-bind="$attrs" ></Innner>
    这里是center:{{ cc }}
  </div>
</template>
<script>
import Innner from "./innner.vue";

export default {
  props: {
    cc: {
      type: String,
    },
  },
  components: {
    Innner,
  },
  mounted(){
  console.log(this.$attrs)
  }
};
</script>

子组件inner:

<template>
  <div>
    这里是inner的aa的值:{{ aa }}<br />
    这里是inner的bb的值:{{ bb }}
  </div>
</template>
<script>
export default {
  props: {
    aa: { type: String, default: "haha" },
    bb: { type: String, default: "heihei" },
  },
  data() {
    return {};
  },
  mounted() {},
};
</script>
<style lang="">
</style>

结果
在这里插入图片描述

$ listerner:包含了爷组件outside中的 (不含 .native 修饰器的) 所有v-on 事件。

它可以通过 v-on=”$listeners” 传入内部组件。简单点来说就是进行爷孙组件之间的 $emit对话。在使用.sync二次封装时不要忘记写它。

爷组件outside

<template>
  <div>
    <Center :aa="a1" :bb="b1" :cc="c1" @onChange="handleChange"></Center>
  </div>
</template>
<script>
import Center from "./center.vue";
export default {
  components: {
    Center,
  },
  data() {
    return {
      c1: "这里是outside组件的c",
      a1: "这里是outside的a",
      b1: "这里是outside的b",
    };
  },
  methods: {
    handleChange(val) {
      this.a1 = val;
    },
  },
};
</script>

父组件center:

    <Innner v-bind="$attrs" v-on="$listeners"></Innner>

孙组件inner

<template>
  <div>
    这里是inner的aa的值:{{ aa }} <button @click="handle">修改aa的值</button>
    <br />
    这里是inner的bb的值:{{ bb }}
  </div>
</template>
<script>
export default {
  props: {
    aa: { type: String, default: "haha" },
    bb: { type: String, default: "heihei" },
  },
  data() {
    return {};
  },
  mounted() {},
  methods: {
    handle() {
      this.$emit("onChange", "修改aa的值为$listeners");
    },
  },
};
</script>

结果
在这里插入图片描述
点击按钮:
在这里插入图片描述

$ parent、$children

$ parent、$children获得的是实例对象,我们需要在实例上获得我们需要的东西

$ parent:当前实例的父实例,如果当前实例有的话。

爷组件outside:

html部分
<Center :aa="a1" :bb="b1" :cc="c1" @onCenter="handleChange"></Center>
script部分
data() {
    return {
      c1: "这里是outside组件的c",
      a1: "这里是outside的a",
      b1: "这里是outside的b",
    };
  },

父组件center :script部分

<script>
import Innner from "./innner.vue";

export default {
  props: {
    cc: {
      type: String,
    },
  },
  components: {
    Innner,
  },
  mounted() {
    console.log(this.$parent);
  },
};
</script>

这里可以通过this.$parent.a1获得爷组件数据
在这里插入图片描述

$children:当前实例的直接子组件。需要注意 $children 并不保证顺序,也不是响应式的。如果你发现自己正在尝试使用 $children 来进行数据绑定,考虑使用一个数组配合 v-for 来生成子组件,并且使用 Array 作为真正的来源。

这里得到的是包含子组件对象的数组

父组件center:script部分

mounted() {
    console.log(this.$parent);
    console.log(this.$children);
  },

同样的,这里可以通过this.$children.aa获得子组件inner的数据
在这里插入图片描述

eventBus

EventBus 又称为事件总线。在Vue中可以使用 EventBus 来作为沟通桥梁的概念,就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件,所以组件都可以上下平行地通知其他组件,但也就是太方便所以若使用不慎,就会造成难以维护的“灾难”,因此才需要更完善的Vuex作为状态管理中心,将通知的概念上升到共享状态层次。
这里可以理解为,组件A在事件总线注册事件,组件B监听事件来实现非父子组件的传值。

初始化

//main.js
Vue.prototype.$EventBus = new Vue()

注册事件:$emit注册事件

//evevtBusA.vue
<template>
  <button @click="sendMsg()">开始</button>
</template>

<script>
export default {
  methods: {
    sendMsg() {
      this.$EventBus.$emit("aMsg", "来自A页面的消息");
    },
  },
};
</script>

监听事件:$on监听

<template>
</template>

<script>
export default {
  data() {
    return {
      msg: "",
    };
  },
  mounted() {
    this.$EventBus.$on("aMsg", (msg) => {
      // A发送来的消息
     console.log("这里是B组件", msg);
    });
  },
};
</script>

点击开始按钮:
在这里插入图片描述
大家都知道vue是单页应用,如果你在某一个页面刷新了之后,与之相关的EventBus会被移除,这样就导致业务走不下去。(能用其他方式就用其他方式吧,我干业务是一次没用到过这个A.A)移出事件监听就不写了,对线面试官知道怎么用事件总线已经够了。详情可以康康这篇文章Vue事件总线(EventBus)使用详细介绍

provide和inject

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

这里我们还是用经典爷父孙组件举例
爷组件outside

<template>
  <div>

    <Center></Center>
  </div>
</template>
<script>
import Center from "./center.vue";
export default {
  components: {
    Center,
  },
  provide: {
    foo: "来自outside的foo的值",
  },
  data() {
    return {
    };
  },
};
</script>
<style lang="">
</style>

孙组件inner:

<template>
  <div>
    这里是inject的foo的值:{{ foo }}
  </div>
</template>
<script>
export default {
  inject: {
    foo: {
      default: () =>'inner默认foo的值',//默认值
    },
  },
};
</script>
<style lang="">
</style>

结果:
在这里插入图片描述
在多个父级组件都provide注入依赖时,例如outside、center都注入依赖,我们可以在子组件inner中选择数据的来源

父组件center:

<template>
  <div>
    <Innner v-bind="$attrs" v-on="$listeners"></Innner>
  </div>
</template>
<script>
import Innner from "./innner.vue";

export default {
  provide: {
     bar: "来自center的foo的值",
  },
  components: {
    Innner,
  },
};
</script>

目前父级组件已经注入了 foo和bar两个变量
子组件inner

<template>
  <div>
    这里是inject的foo的值:{{ foo }}
  </div>
</template>
<script>
export default {
  props: {
    aa: { type: String, default: "haha" },
    bb: { type: String, default: "heihei" },
  },
  inject: {
 	//添加defualt使其变为可选项
  foo:{
	from:'bar',//来源从outside的foo变为center的bar
	defualt:'来自inner的foo的值'
}
  }
};
</script>

结果:
在这里插入图片描述

localStorage

这里没啥好说的,用sessionStorege也可以。
父组件window.localStorage.setItem储存变量
子组件window.localStorage.getItem获得变量
outside

<template>
  <div>

    <Center ></Center>
  </div>
</template>
<script>
import Center from "./center.vue";
export default {
  components: {
    Center,
  },
  data() {
    return {
      stroregeMsg: "来自outside的Msg",
    };
  },
  mounted() {
    window.localStorage.setItem("Msg", this.stroregeMsg);
  },
};
</script>
<style lang="">
</style>

inner

<template>
  <div>
    这里是inner的msg的值:{{ msg }}
  </div>
</template>
<script>
export default {
  data() {
    return {
      msg: "",
    };
  },
  mounted() {
    this.msg = window.localStorage.getItem("Msg");
  },
};
</script>

结果:
在这里插入图片描述
在这里插入图片描述

Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
可以简单理解为一个公用的状态管理中心。推荐vuex官网
vuex文件

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    vuexMsg: ""
  },
  mutations: {
    getVuexMgs: function (state, val) {
      state.vuexMsg = val
    }
  },
  actions: {
  },
  modules: {
  }
})

outside:

<template>
  <div>
    <Center :aa="a1" :bb="b1" :cc="c1" @onCenter="handleChange"></Center>
  </div>
</template>
<script>
import Center from "./center.vue";
export default {
  components: {
    Center,
  },
  data() {
    return {
      vuexMsg: "来自outside的vuexMsg",
    };
  },
  mounted() {
    this.$store.commit("getVuexMgs", this.vuexMsg);
  },
};
</script>
<style lang="">
</style>

inner:

<template>
  <div>
    这里是inner的vuexmsg的值:{{ this.$store.state.vuexMsg }}
  </div>
</template>
<script>
export default {
  data() {
    return {
    };
  },

};
</script>

结果:
在这里插入图片描述

结语

在干业务对话框的二次封装时,使用props,$emit封装那个麻烦程度让我备受打击,一度以为自己傻了。于是想有无更加快捷的传值方式。干脆一不做二不休,做一次组件传值的技术总结。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值