阅读此章节时,我们假设你已经读过组件基础,若你对组件还完全不了解,请先阅读它。
Prop Drilling
通常情况下,当我们需要从父组件向子组件传递数据时,会使用 props。想象一下这样的结构:有一些多层级嵌套的组件,形成了一颗巨大的组件树,而某个深层的子组件需要一个较远的祖先组件中的部分内容。在这种情况下,如果仅使用 props 则必须将其沿着组件链逐级传递下去,这会非常麻烦:
provideinject
Provide (供给)
<script setup>
import { provide } from 'vue'
provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
</script>
import { provide } from 'vue'
export default {
setup() {
provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
}
}
import { ref, provide } from 'vue'
const count = ref(0)
provide('key', count)
export default {
provide: {
message: 'hello!'
}
}
export default {
data() {
return {
message: 'hello!'
}
},
provide() {
// 使用函数的形式,可以访问到 `this`
return {
message: this.message
}
}
}
应用层 Provide
除了供给一个组件的数据,我们还可以在整个应用层面做供给:
import { createApp } from 'vue'
const app = createApp({})
app.provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
应用级的供给在应用的所有组件中都可以注入。这在你编写插件时会特别有用,因为插件一般都不会使用组件形式来供给值。
Inject (注入)
<script setup>
import { inject } from 'vue'
const message = inject('message')
</script>
import { inject } from 'vue'
export default {
setup() {
const message = inject('message')
return { message }
}
}
export default {
inject: ['message'],
created() {
console.log(this.message) // injected value
}
}
export default {
inject: ['message'],
data() {
return {
// 基于注入值的初始数据
fullMessage: this.message
}
}
}
export default {
inject: {
/* 局部注入名 */ localMessage: {
from: /* 原注入名 */ 'message'
}
}
}
注入的默认值 #
inject
如果在供给的一侧看来属性是可选提供的,那么注入时我们应该声明一个默认值,和 props 类似:
// 如果没有祖先组件提供 "message"
// `value` 会是 "这是默认值"
const value = inject('message', '这是默认值')
const value = inject('key', () => new ExpensiveClass())
export default {
// 当声明注入的默认值时
// 必须使用对象形式
inject: {
message: {
from: 'message', // 当与原注入名同名时,这个属性是可选的
default: 'default value'
},
user: {
// 对于非基础类型数据,如果创建开销比较大,或是需要确保每个组件实例
// 需要独立数据的,请使用工厂函数
default: () => ({ name: 'John' })
}
}
}
配合响应性
<!-- 在 provider 组件内 -->
<script setup>
import { provide, ref } from 'vue'
const location = ref('North Pole')
function updateLocation() {
location.value = 'South Pole'
}
provide('location', {
location,
updateLocation
})
</script>
<!-- 在 injector 组件 -->
<script setup>
import { inject } from 'vue'
const { location, updateLocation } = inject('location')
</script>
<template>
<button @click="updateLocation">{{ location }}</button>
</template>
<script setup>
import { ref, provide, readonly } from 'vue'
const count = ref(0)
provide('read-only-count', readonly(count))
</script>
import { computed } from 'vue'
export default {
data() {
return {
message: 'hello!'
}
},
provide() {
return {
// 显式提供一个计算属性
message: computed(() => this.message)
}
}
}
使用 Symbol 作注入名
至此,我们已经了解了如何使用字符串作为注入名。但如果你正在构建大型的应用程序,包含非常多的依赖供给,或者你正在编写提供给其他开发者使用的组件库,建议最好使用 Symbol 来作为注入名以避免潜在的冲突。
建议在一个单独的文件中导出这些注入名 Symbol:
// keys.js
export const myInjectionKey = Symbol()
// 在供给方组件中
import { provide } from 'vue'
import { myInjectionKey } from './keys.js'
provide(myInjectionKey, { /*
要供给的数据
*/ });
// 注入方组件
import { inject } from 'vue'
import { myInjectionKey } from './keys.js'
const injected = inject(myInjectionKey)
// 在供给方组件中
import { myInjectionKey } from './keys.js'
export default {
provide() {
return {
[myInjectionKey]: {
/* 要供给的数据 */
}
}
}
}
// 注入方组件
import { myInjectionKey } from './keys.js'
export default {
inject: {
injected: { from: myInjectionKey }
}
}
__译者注__ [1] 在本章及后续章节中,“**提供**”将成为对应 Provide 的一个专有概念