《Vue3 六》组件间通信
父组件通过 props 属性给子组件传递数据:
可以在组件上注册一些自定义的 attribute,父组件给 attribute 赋值,子组件通过 attribute 的名称获取对应的值。
// App.vue
<template><!-- 1. 父组件通过属性给子组件传递数据 --><UserInfo name='Lee' age=18 height="1.88 "/>
</template><script>
import UserInfo from './components/UserInfo'export default {components: {UserInfo,}
}
</script><style scoped>
</style>
// UserInfo.vue
<template><div><div>姓名:{{ name }}</div><div>年龄:{{ age }}</div><div>身高:{{ height }}</div></div>
</template><script>
export default {// 2. 子组件通过 props 选项接收父组件传递过来的数据props: ['name', 'age', 'height'],
}
</script><style scoped>
</style>
props 的常见写法:
- 字符串数组:
export default {props: ['name', 'age', 'height'], }
- 对象,可以指定数据的默认值、类型、是否是必须的。
export default {props: {name: String,age: [String, Number], // 多个可能的类型height:{default: 1.88,type: Number, // type 常见的类型:String、Number、Boolean、Array、Object、Date、Function、Symbol。如果类型是 Array 或者 Object,要写默认值的话,默认值必须是一个函数,函数的返回值是数组或对象;如果类型是 Function,要写默认值的话,default 直接写作一个函数即可。required: true,}} }
props 自定义验证函数:
export default {props: {// 数据的值必须匹配下列数组中的一个validator(value) {return ['success', 'warning', 'danger'].includes(value)}}
}
非 prop 的 attribute:
非 prop 的 attribute:当给组件传递了某个属性,但是该属性并没有定义对应的 props 或者 emits 时,就称之为非 prop 的 attribute。常见的有 class、style、id 等。
当组件有单个根元素时,非 prop 的 attribute 会被自动添加到组件的根元素上。当组件有多个根元素且非 prop 的 attribute 没有被显式地绑定,那么会报一个警告。
// App.vue
<template><!-- 给 UserInfo 组件传递了属性 id,但是并没有定义对应的 props 或者 emits --><UserInfo name='Lee' age=18 height="1.88" id="99"/>
</template><script>
import UserInfo from './components/UserInfo'export default {components: {UserInfo,}
}
</script><style scoped>
</style>
// UserInfo.vue
<template><div><div>姓名:{{ name }}</div><div>年龄:{{ age }}</div><div>身高:{{ height }}</div></div>
</template><script>
export default {props: ['name', 'age', 'height'],
}
</script><style scoped>
</style>
如果不希望组件的根元素继承非 prop 的 attribute,可以在组件中设置 inheritAttrs: false
。
<template><div><div>姓名:{{ name }}</div><div>年龄:{{ age }}</div><div>身高:{{ height }}</div></div>
</template><script>
export default {inheritAttrs: false,props: ['name', 'age', 'height'],
}
</script><style scoped>
</style>
所有的非 prop 的 attribute 都可以通过 $attrs
来访问。
<template><div><div :id="$attrs.id">姓名:{{ name }}</div><div>年龄:{{ age }}</div><div>身高:{{ height }}</div></div>
</template><script>
export default {props: ['name', 'age', 'height'],
}
</script><style scoped>
</style>
子组件通过 $emit
触发事件给父组件传递数据:
首先在子组件中通过 $emit 发出的事件,然后在父组件中通过 v-on 监听发出的事件。
// App.vue
<template><div>{{ count }}</div><!-- 在父组件中通过 v-on 监听发出的事件 --><AddCount @add="addHandler" />
</template><script>
import AddCount from './components/AddCount'export default {components: {AddCount,},data() {return {count: 0,}},methods: {addHandler(value) {this.count += value}}
}
</script><style scoped>
</style>
// AddCount.vue
<template><button @click="handleAdd(1)">+1</button><button @click="handleAdd(50)">+50</button><button @click="handleAdd(100)">+100</button>
</template><script>
export default {methods: {handleAdd(value) {// 1. 在子组件中通过 $emit 发出事件,第一个参数是事件的名称,第二个参数是事件的参数this.$emit('add', value)}}
}
</script><style scoped>
</style>
通过 Provide/Inject 进行祖先后代组件通信:
Provide/Inject 用于非父子组件之间共享数据。无论层级结构有多深,祖先组件都可以作为其所有后代组件的依赖者,组件组件有一个 Provide 选项来提供数据,后代组件有一个 Inject 选项来获取数据。
// App.vue
<template><Home></Home>
</template><script>
import Home from './components/Home'export default {components: {Home,},// 在祖先组件中,通过 provide 选项提供数据。如果 provide 中的数据来自 data 等选项,需要通过 this 获取,那么 provide 需要写成一个函数,返回值是一个对象provide: {name: 'Lee',age: 18,},
}
</script><style scoped>
</style>
// Home.vue
<template><HomeBanner></HomeBanner>
</template><script>
import HomeBanner from './HomeBanner'export default {components: {HomeBanner,},
}
</script><style scoped>
</style>
<template><div>姓名:{{name}}</div><div>年龄:{{age}}</div>
</template><script>export default {// 在后代组件中,通过 inject 选项获取数据inject: ['name', 'age']}</script><style scoped></style>
通过事件总线进行组件通信:
Vue3 官方推荐了一些事件总线的库:mitt 或者 tiny-emitter
。
Vue2 中原生就有提供事件总线的功能;但是在 Vue3 中从实例中移除了
$on
、$off
、$once
方法,去除了事件总线的功能。