在 Vue 3 中,provide
和 inject
是一对用于实现依赖注入的 API。它们提供了一种方式,让祖先组件能够向其所有子孙后代组件注入依赖,而无需通过 props
逐层传递。这在开发大型复杂应用时,尤其是当组件层级较深时,可以极大地简化代码和提高可维护性。
1. 基本用法
1.1 provide
provide
选项应该是一个对象或返回一个对象的函数。该对象包含可注入其子级的属性。你可以将这些属性视为从祖先组件“提供”给后代组件的值或函数。
在 setup
函数内部使用 provide
时,需要从 vue
包中导入它。
javascript复制代码
import { provide, ref } from 'vue'; | |
export default { | |
setup() { | |
const theme = ref('dark'); | |
provide('theme', theme); | |
// 其他逻辑... | |
}, | |
}; |
在 setup
之外,你可以直接在组件选项中定义 provide
:
javascript复制代码
export default { | |
data() { | |
return { | |
theme: 'dark', | |
}; | |
}, | |
provide() { | |
return { | |
theme: this.theme, | |
}; | |
}, | |
}; |
1.2 inject
inject
函数用来在子组件中接收由祖先组件通过 provide
提供的值。第一个参数是提供的 key(字符串),第二个参数是可选的默认值。
在 setup
函数中使用 inject
:
javascript复制代码
import { inject } from 'vue'; | |
export default { | |
setup() { | |
const theme = inject('theme', 'light'); // 如果没有注入 'theme',则使用默认值 'light' | |
// 使用 theme... | |
}, | |
}; |
2. 高级用法
2.1 响应性
在 Vue 3 中,通过 ref
、reactive
或 computed
提供的值是响应性的,意味着当提供的值改变时,任何使用 inject
接收该值的组件都将更新。
2.2 符号作为 key
为了避免 key 冲突,你可以使用 Symbol 作为 provide 和 inject 的 key。这确保了你的 provide 是唯一的,不会被其他组件意外覆盖。
javascript复制代码
import { inject, provide, ref, Symbol } from 'vue'; | |
const ThemeSymbol = Symbol('theme'); | |
export default { | |
setup() { | |
const theme = ref('dark'); | |
provide(ThemeSymbol, theme); | |
// ... | |
}, | |
}; | |
// 在子组件中 | |
export default { | |
setup() { | |
const theme = inject(ThemeSymbol, 'light'); | |
// 使用 theme... | |
}, | |
}; |
2.3 provide 的函数形式
provide
也可以是一个返回对象的函数,这在结合 setup
函数中的响应性状态时非常有用。函数将接收组件的 props
和 context
参数。
然而,通常直接在 setup
函数内部使用 provide
函数形式是不必要的,因为你可以直接访问响应性状态和函数。但如果你需要在 setup
之外定义 provide
,并且需要访问组件实例(如 this
),则函数形式是有用的。
不过,在 Vue 3 的组合式 API 中,你通常会直接在 setup
函数内部工作,因此不太可能需要这种用法。
3. 注意事项
- 不是响应式的值(如基本类型的字面量、普通的对象等)在传递给
provide
后将不会更新子组件中的相应值,除非它们被封装成响应式对象(如使用ref
或reactive
)。 - 尽量避免在应用中过度使用依赖注入,因为它可以使组件间的关系变得难以追踪和理解。通常,在开发大型库或框架时,依赖注入才显得尤为有用。
provide
和inject
主要用于开发高阶组件、库或高级用途。在日常开发中,应优先考虑使用props
和emit
来传递数据和事件。- 当使用
provide
和inject
时,确保提供清晰的文档和类型定义(如果使用 TypeScript),以帮助其他开发者理解这些依赖是如何工作的。
4. 示例:主题切换器
下面是一个简单的例子,演示了如何使用 provide
和 inject
在 Vue 3 应用中实现主题切换功能。
祖先组件(App.vue):
vue复制代码
<template> | |
<div :class="theme"> | |
<ThemeSwitcher /> | |
<MainContent /> | |
</div> | |
</template> | |
<script> | |
import { ref, provide } from 'vue'; | |
import ThemeSwitcher from './components/ThemeSwitcher.vue'; | |
import MainContent from './components/MainContent.vue'; | |
export default { | |
components: { | |
ThemeSwitcher, | |
MainContent, | |
}, | |
setup() { | |
const theme = ref('light'); | |
const toggleTheme = () => { | |
theme.value = theme.value === 'light' ? 'dark' : 'light'; | |
}; | |
provide('theme', theme); | |
provide('toggleTheme', toggleTheme); | |
return { theme }; | |
}, | |
}; | |
</script> | |
<style> | |
.light { | |
background-color: white; | |
color: black; | |
} | |
.dark { | |
background-color: black; | |
color: white; | |
} | |
</style> |
子组件(ThemeSwitcher.vue):
vue复制代码
<template> | |
<button @click="toggleTheme">Switch Theme</button> | |
</template> | |
<script> | |
import { inject } from 'vue'; | |
export default { | |
setup() { | |
const toggleTheme = inject('toggleTheme'); | |
return { toggleTheme }; | |
}, | |
}; | |
</script> |
另一个子组件(MainContent.vue):
vue复制代码
<template> | |
<div> | |
<p>This is the main content.</p> | |
<p>Current theme: {{ theme }}</p> | |
</div> | |
</template> | |
<script> | |
import { inject } from 'vue'; | |
export default { | |
setup() { | |
const theme = inject('theme'); | |
return { theme }; | |
}, | |
}; | |
</script> |
在这个例子中,App.vue
组件提供了 theme
响应式引用和 toggleTheme
函数。然后,任何子组件都可以通过 inject
来接收并使用这些值或函数。ThemeSwitcher.vue
组件注入并使用 toggleTheme
函数来切换主题,而 MainContent.vue
组件则注入并显示当前的主题。