Vue3 组件通讯的方式
Vue3 组件通讯的方式
- props 和 emits【子组件通过props:[]接收父组件的属性,子组件通过emits:[]获取父组件方法】
- 自定义事件 $emit【子组件通过$emit 向父组件传值】
- $attrs【子组件通过 this.$attrs 可以获取父组件上的属性和方法】
- $parent【子组件通过 this.$parent 可以获取父组件上的属性和方法】
- $refs【父组件通过 this.$refs 可以获取子组件上的属性和方法】
- provide/inject【上级 provide 传递,下级 inject 接收】
- Vuex
Vue3 组件通讯适用不同场景
- ❶ 父子组件【子取父 props、emits、$attrs、$parent;父取子 $emit、$refs】
- ❷ 上下级组件(跨多级)通讯【provide/inject】
- ❸ 全局组件【event-emitter/Vuex】
vue3.0 父子组件通信
父组件往子组件传值 props
父组件
<template>
<div class="parent">
<Child :child-com = "content"></Child>
</div>
</template>
<script>
// 引入子组件
import Child from './child.vue';
export default {
components: {
Child
},
data () {
return {
content: 'this is from parent'
}
}
}
</script>
传递的参数名称不识别驼峰命名,推荐使用横杠-命名
子组件 child.vue
<template>
<div id="child">
</div>
</template>
<script>
export default {
props: {
childCom: {
type: String,
default: 'i will from parent'
}
}
}
</script>
$emit 事件
子组件往父组件传值,通过emit事件
子组件
<template>
<div id="child">
<div @click="open">click</div>
</div>
</template>
<script>
export default {
emits: ['showbox'], // Vue3新增的
methods: {
open() {
this.$emit('showbox', 'child need parent show')
}
}
}
</script>
父组件
<template>
<div class="parent">
<Child @showbox="toshow"></Child>
</div>
</template>
<script>
// 引入子组件
import Child from './child.vue';
export default {
components: {
Child
},
methods: {
toshow(msg) {
console.log(msg)
}
}
}
</script>
$attrs
子组件通过 this.$attrs 可以获取父组件上的属性和方法
$attrs 是 props 和 emits 之外的候补。
$attrs 具体示例
components
- Level1.vue
- Level2.vue
-
Level3.vue
- 父组件 Level1.vue 包含子组件 Level2.vue, 子组件 Level2.vue 包含孙子组件 Level3.vue
- Level1.vue 向 Level2.vue 传递 a、b、c、getA、getB、getC
- Level2.vue 向 Level3.vue 传递 x、y、z、getX、getY、getZ
【解释:】
- Level1 向 Level2 传递的属性 a、b、c, 可以在 Level2 页面中通过 props: [‘a’] 获取,
- Level1 向 Level2 传递的方法 getA、getB、getC, 可以在 Level2 页面中通过 emits: [‘getA’] 获取,
- Level1 向 Level2 传递的属性和方法(a、b、c、getA、getB、getC),没有通过 props(a)、emits(getA) 接收的,可以在 Level2 页面中通过 $attrs 获取到(b、c、getB、getC),
- 如果 Level2 想把父组件 Level1 给它传递的属性和方法再传递给孙子组件 Level3 ,可以在 Level2 组件上添加 v-bind=”$attrs” 属性
Level1.vue
<template>
<p>Level1</p>
<Level2
:a="a"
:b="b"
:c="c"
@getA="getA"
@getB="getb"
@getC="getC"
></Level2>
</template>
<script>
import Level2 from './Level2'
export default {
name: 'Level1',
components: { Level2 },
data() {
return {
a: "aaa",
b: "bbb",
c: "ccc"
},
},
methods: {
getA() {
return this.a
},
getB() {
return this.b
},
getC() {
return this.c
},
}
}
</script>
Level2.vue
<template>
<p>Level2</p>
<Level3
:x="x"
:y="y"
:z="z"
@getX="getX"
@getY="getY"
@getZ="getZ"
v-bind="$attrs"
></Level3>
</template>
<script>
import Level2 from './Level2'
export default {
name: 'Level2',
components: { Level3 },
props: ['a'],
emits: ['getA']
data() {
return {
x: "xxx",
y: "yyy",
z: "zzz"
},
methods: {
getX() {
return this.x
},
getY() {
return this.y
},
getZ() {
return this.z
},
},
created() {
console.log(this.$attrs) // Proxy{ b:"bbb", c:"ccc", onGetB: f, onGetC: f }
},
}
}
</script>
父组件 Level1.vue 向子组件 Level2.vue 传递,如果子组件 Level2.vue 里面 props: [‘a’], emits: [‘getA’] 没有接收,那么其他属性和方法就会被 this.$attrs 捕获到。
Level2.vue 中 v-bind=”$attrs” 可以把 b、c、onGetB、onGetC 再次传递给子组件 Level3.vue
Level3.vue
<template>
<p>Level3</p>
</template>
<script>
export default {
name: 'Level3',
props: ['x'],
emits: ['getX']
data() {
return {
},
},
created() {
console.log(this.$attrs) // Proxy{ y:"yyy", z:"zzz", onGetY: f, onGetZ: f }
},
}
</script>
$parent
子组件通过 this.$parent 可以获取父组件上的属性和方法
也是以上 Level3.vue 页面获取父组件 Level2.vue
Level3.vue
<template>
<p>Level3</p>
</template>
<script>
export default {
name: 'Level3',
props: ['x'],
emits: ['getX']
data() {
return {
},
},
created() {
console.log(this.$attrs) // Proxy{ y:"yyy", z:"zzz", onGetY: f, onGetZ: f }
},
mounted() {
console.log(this.$parent);
console.log(this.$parent.x);
console.log(this.$parent.getX);
}
}
</script>
$refs
父组件通过 this.$refs 可以获取子组件上的属性和方法
父组件 Level3.vue 获取子组件 HelloWorld.vue 上的属性和方法
注意点:$parent 和 $refs 需要在 mounted方法上用, 而不是 created 方法上用。
Level3.vue
<template>
<p>Level3</p>
<HelloWorld msg="hello pyy" ref="hello1"></HelloWorld>
</template>
<script>
import HelloWorld from './HelloWorld'
export default {
name: 'Level3',
components: { HelloWorld },
props: ['x'],
emits: ['getX']
data() {
return {
},
},
mounted() {
console.log(this.$refs.hello1);
console.log(this.$refs.hello1.name);
}
}
</script>
HelloWorld.vue
<template>
<p></p>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
},
data() {
return {
name: 'hello-world'
}
}
}
</script>
多层级通讯 provide/inject
上级 provide 传递,下级 inject 接收
上级组件通过 provide: {info: "xxx"} 定义,下级可以通过 inject: ['info'] 获取上级传递下来的信息。
示例: 父组件 Level1 包含子组件 Level2,子组件 Level2 包含孙组件 Level3
Level1.vue
<template>
<p>Level1</p>
<input v-model="name">
<Level2></Level2>
</template>
<script>
import { computed } from 'vue'
import Level2 from "./Level2"
export default {
name: 'Level1',
components: { Level2 },
data() {
return {
name: 'pyy'
},
},
// 传递静态数据
// provide: {
// info: 'aaa'
// },
// 用函数的方式传递响应式的数据
provide() {
return {
info: computed(() => this.name)
}
}
}
</script>
Level2.vue
<template>
<p>Level2 </p>
<Level3></Level3>
</template>
<script>
import Level3 from "./Level3"
export default {
name: 'Level2',
components: { Level3 },
inject: ['info']
}
</script>
Level3.vue
<template>
<p>Level3</p>
</template>
<script>
export default {
name: 'Level3',
inject: ['info']
}
</script>
全局组件之间传值,通过 event-emitter
小项目少页面用 event-emitter,大项目多页面使用 vuex
示例:childA向childB发送数据
components
- eventBus.js
- childA.vue
- childB.vue
Vue3 中的 eventBus.js
// Vue2 new Vue() 就是 event
// Vue3 引入第三方的自定义事件
import ee from 'event-emitter';
const event = ee();
export default event;
childA.vue 发送命令
<template>
<div id="child">
<div @click="ge">i will send a message to childB</div>
</div>
</template>
<script>
import eventBus from './eventBus.js'
export default {
methods: {
ge() {
eventBus.emit('eventFromA', '小弟好')
}
}
}
</script>
childB.vue 接收命令
<template>
<div id="child">
</div>
</template>
<script>
import eventBus from './eventBus.js'
export default {
methods: {
showMsg(msg) {
console.log(msg)
}
},
created() {
eventBus.on('eventFromA', this.showMsg)
},
beforeUnmount() {
eventBus.off('eventFromA', this.showMsg)
}
}
</script>