提供/注入
本页面假设您已经阅读了组件基础。如果是初次接触组件,请先阅读。
属性钻取
通常,当我们需要从父组件传递数据到子组件时,我们会使用props。但是,想象一下我们有一个庞大的组件树,并且一个深层嵌套的组件需要从遥远的祖先组件获取某些内容。仅使用props,我们可能不得不在整个父链上传递相同的props
注意,尽管<Footer>
组件可能根本不关心这些props,但它仍然需要声明并传递它们,以便<DeepChild>
可以访问它们。如果父链更长,那么在传递过程中会有更多组件受到影响。这被称为“属性钻取”,并且处理起来绝对不有趣。
我们可以使用provide
和inject
来解决属性钻取问题。父组件可以充当所有其后代组件的依赖提供者。后代树中的任何组件,无论其深度如何,都可以从其父链中的组件中注入依赖项。
提供
要将数据提供给组件的后代,请使用provide()
函数
vue
<script setup>
import { provide } from 'vue'
provide(/* key */ 'message', /* value */ 'hello!')
</script>
如果不使用<script setup>
,请确保在setup()
内部同步调用provide()
js
import { provide } from 'vue'
export default {
setup() {
provide(/* key */ 'message', /* value */ 'hello!')
}
}
provide()
函数接受两个参数。第一个参数称为注入键,可以是字符串或Symbol
。注入键用于后代组件查找要注入的所需值。单个组件可以使用不同的注入键多次调用provide()
以提供不同的值。
第二个参数是提供的值。值可以是任何类型,包括像refs这样的响应式状态。
js
import { ref, provide } from 'vue'
const count = ref(0)
provide('key', count)
提供响应式值允许使用提供的值的后代组件与提供者组件建立响应式连接。
应用级提供
除了在组件中提供数据外,我们还可以在应用级别提供
js
import { createApp } from 'vue'
const app = createApp({})
app.provide(/* key */ 'message', /* value */ 'hello!')
应用级提供适用于应用中渲染的所有组件。这在编写插件时特别有用,因为插件通常无法使用组件提供值。
注入
要注入祖先组件提供的数据,请使用inject()
函数
vue
<script setup>
import { inject } from 'vue'
const message = inject('message')
</script>
如果提供的值是ref,它将被原样注入,并且将不会被自动解包。这允许注入组件保留与提供者组件的响应式连接。
再次提醒,如果不使用<script setup>
,则应在setup()
内部同步调用inject()
js
import { inject } from 'vue'
export default {
setup() {
const message = inject('message')
return { message }
}
}
注入默认值
默认情况下,inject
假设注入键在父链的某个地方提供。如果键没有提供,将会有运行时警告。
如果我们想使注入属性与可选提供者一起工作,我们需要声明一个默认值,类似于 props
js
// `value` will be "default value"
// if no data matching "message" was provided
const value = inject('message', 'default value')
在某些情况下,默认值可能需要通过调用函数或创建一个新的类来创建。为了避免在可选值未使用时产生不必要的计算或副作用,我们可以使用工厂函数来创建默认值
js
const value = inject('key', () => new ExpensiveClass(), true)
第三个参数表示默认值应被视为工厂函数。
处理响应性
当使用响应式 provide / inject 值时,建议尽可能在 provider 内部进行任何对响应式状态的修改。这确保了提供的状态及其可能的修改位于同一组件中,便于未来的维护。
有时我们需要从注入组件更新数据。在这种情况下,我们建议提供一个负责修改状态的函数
vue
<!-- inside provider component -->
<script setup>
import { provide, ref } from 'vue'
const location = ref('North Pole')
function updateLocation() {
location.value = 'South Pole'
}
provide('location', {
location,
updateLocation
})
</script>
vue
<!-- in injector component -->
<script setup>
import { inject } from 'vue'
const { location, updateLocation } = inject('location')
</script>
<template>
<button @click="updateLocation">{{ location }}</button>
</template>
最后,如果您想确保通过 provide
传递的数据不能被注入组件修改,可以将提供的值包裹在 readonly()
中
vue
<script setup>
import { ref, provide, readonly } from 'vue'
const count = ref(0)
provide('read-only-count', readonly(count))
</script>
处理符号键
到目前为止,我们在示例中一直使用字符串注入键。如果您正在处理一个具有许多依赖提供者的大型应用程序,或者您正在编写将被其他开发者使用的组件,最好使用符号注入键以避免潜在的冲突。
建议在专用文件中导出符号
js
// keys.js
export const myInjectionKey = Symbol()
js
// in provider component
import { provide } from 'vue'
import { myInjectionKey } from './keys.js'
provide(myInjectionKey, {
/* data to provide */
})
js
// in injector component
import { inject } from 'vue'
import { myInjectionKey } from './keys.js'
const injected = inject(myInjectionKey)
另请参阅: 类型 Provide / Inject