目录
props 和 $emit
适合父子组件,父组件给子组件传递数据和函数,传函数时,子组件可返回数据给父组件
示例
// parent 里
<Child :list="list" @add="addHandler"></Child> // Vue 里传数据用:,传函数用@,内容用引号,且不加 this
// child 里
export default {
props: { // Vue 里子组件用 props 获取父组件传来的数据
list: { // 类型检查和默认值
type: PropTypes.arrayof(PropTypes.object).isRequired,
default() {
return []
}
}
},
emits: ['add'], // vue3 新增要写的
methods: {
addInChild(){
console.log(this.list) // 子组件下面直接从 this 里获取数据
this.$emit("add", this.title) // 用 $emit 调用父组件传来的函数
}
}
}
$attrs($listeners)
适合父子组件和上下级(跨多级)组件(不是很完美),props 和 emits 的后补,子组件没有在 props 和 emits 里接收的数据和函数,可以在 $attrs 和 $listeners 里获取到
Vue3 把 $listeners 合并到了 $attrs 里
v-bind="$attrs" 可把爷辈组件的 $attrs 数据透传给孙子组件,实现爷孙组件通讯,但依赖于 v-bind="$attrs",并不是很方便
inheritAttrs: false 可避免把 $attrs 的数据绑到 dom 节点上,注意正常只在有一个 dom 元素的时候才会绑定
示例
// level1
<Level2
:a="a"
:b="b"
:c="c"
@getA="getA"
@getB="getB"
@getC="getC"
></Level2>
data() {
return {
a: 'aaa',
b: 'bbb',
c: 'ccc'
}
},
methods: {
getA() {
return this.a
},
getB() {
return this.b
},
getC() {
return this.c
}
}
// level2
<Level3
:x="x"
:y="y"
:z="z"
@getX="getX"
@getY="getY"
@getZ="getZ"
v-bind="$attrs" // 把爷辈组件的 $attrs 数据透传给孙子组件
></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) // b、c、getB、getC
}
// level3
<p>level3</p>
props: ['x'],
emits: ['getX'],
inheritAttrs: false, // 避免把 $attrs 的数据绑到 dom 节点上
created() {
console.log(this.$attrs) // b、c、getB、getC、y、z、getY、getZ
}
provide/inject
完美的上下级(跨多级)组件通讯方式,上级组件用 provide 提供数据,下级组件用 inject 接收数据
示例
// level1
import { computed } from 'vue'
<Level2></Level2>
provide: {
info: 'aaa'
}
provide() { // 如果提供的是一个变量,需要这样写
return {
info: computed(() => this.name)
}
}
// level2
<Level3></Level3>
// level3
<p>{{ info }}</p>
inject: ['info ']
$parent、$refs($children)
适合父子组件,用 this.$parent 可直接获取父组件,用 $refs 获取子组件
获取子组件,Vue2 里有 $children、Vue3 建议用 $refs,注意要在 mounted 里获取,不能在 created 里,因为 created 里组件还没渲染完成
示例
// level1
<Level2 ref='level2'></Level2>
mounted() { // 注意要在 mounted 里获取,不能在 created 里
console.log(this.$refs.level2.x) // this.$refs 获取到子组件,然后获取其属性
}
// level2
mounted() {
console.log(this.$parent.a) // this.$parent 获取到父组件,然后获取其属性
this.$parent.getA() // this.$parent 获取到父组件,然后调用其方法
}
自定义事件
适合全局不相关组件通讯,以及多个组件触发多个组件响应的情况
示例
// Vue2 里实例已经实现了自定义事件 API
const event = new Vue()
// Vue3 里需要引入第三方自定义事件
import ee from 'event-emitter'
const event = new ee()
// 触发事件
event.$emit('onAddTitle', this.title)
// 绑定响应事件
event.$on('onAddTitle', this.addTitleHandler) // 使用 this.addTitle 函数名字而不是匿名函数是为了销毁组件时解绑
// 解绑,避免内存泄漏
beforeDestroy() { // Vue3 里改成了 beforeUnmount
event.$off('onAddTitle', this.addTitleHandler)
}
Vuex
适合全局不相关组件共享数据
总结
最常用的是 props 和 $emit,用于父子组件数据传递,父组件用 props 传数据和函数给子组件,子组件调用函数可回传数据给父组件 ->
对于没有显式接收的 props,可用 $attrs 接收到,用 v-bind 可再传递给孙子组件,但用这种方式进行上下级组件传递不够完美,因为严重依赖 v-bind ->
provide/inject 是完美的跨级传递数据的方式,上级组件用 provide 提供数据,下级组件用 inject 接收数据 ->
父子组件传递还有一种 $parent、$refs 直接获取父子组件的方式 ->
对于全局无关联组件,可用自定义事件实现事件和数据通讯 ->
全局纯数据同步可用 Vuex 实现