提供/注入

本指南假定你已经阅读了 Provide / InjectComposition API Introduction响应式基础。如果你不熟悉组合 API,请先阅读这篇文章。

我们也可以在 Composition API 中使用 provide/inject。两者都只能在当前活动实例的 setup() 期间调用。

设想场景

假设我们要重写以下代码,其中包含一个 MyMap 组件,该组件使用 Composition API 为 MyMarker 组件提供用户的位置。

  1. <!-- src/components/MyMap.vue -->
  2. <template>
  3. <MyMarker />
  4. </template>
  5. <script>
  6. import MyMarker from './MyMarker.vue'
  7. export default {
  8. components: {
  9. MyMarker
  10. },
  11. provide: {
  12. location: 'North Pole',
  13. geolocation: {
  14. longitude: 90,
  15. latitude: 135
  16. }
  17. }
  18. }
  19. </script>
  1. <!-- src/components/MyMarker.vue -->
  2. <script>
  3. export default {
  4. inject: ['location', 'geolocation']
  5. }
  6. </script>

使用 Provide

setup() 中使用 provide 时,我们首先从 vue 显式导入 provide 方法。这使我们能够调用 provide 时来定义每个 property。

provide 函数允许你通过两个参数定义 property:

  1. property 的 name (<String> 类型)
  2. property 的 value

使用 MyMap 组件,我们提供的值可以按如下方式重构:

  1. <!-- src/components/MyMap.vue -->
  2. <template>
  3. <MyMarker />
  4. </template>
  5. <script>
  6. import { provide } from 'vue'
  7. import MyMarker from './MyMarker.vue
  8. export default {
  9. components: {
  10. MyMarker
  11. },
  12. setup() {
  13. provide('location', 'North Pole')
  14. provide('geolocation', {
  15. longitude: 90,
  16. latitude: 135
  17. })
  18. }
  19. }
  20. </script>

使用注入

setup() 中使用 inject 时,还需要从 vue 显式导入它。一旦我们这样做了,我们就可以调用它来定义如何将它暴露给我们的组件。

inject 函数有两个参数:

  1. 要注入的 property 的名称
  2. 一个默认的值 (可选)

使用 MyMarker 组件,可以使用以下代码对其进行重构:

  1. <!-- src/components/MyMarker.vue -->
  2. <script>
  3. import { inject } from 'vue'
  4. export default {
  5. setup() {
  6. const userLocation = inject('location', 'The Universe')
  7. const userGeolocation = inject('geolocation')
  8. return {
  9. userLocation,
  10. userGeolocation
  11. }
  12. }
  13. }
  14. </script>

响应性

添加响应性

为了增加提供值和注入值之间的响应性,我们可以在提供值时使用 refreactive

使用 MyMap 组件,我们的代码可以更新如下:

  1. <!-- src/components/MyMap.vue -->
  2. <template>
  3. <MyMarker />
  4. </template>
  5. <script>
  6. import { provide, reactive, ref } from 'vue'
  7. import MyMarker from './MyMarker.vue
  8. export default {
  9. components: {
  10. MyMarker
  11. },
  12. setup() {
  13. const location = ref('North Pole')
  14. const geolocation = reactive({
  15. longitude: 90,
  16. latitude: 135
  17. })
  18. provide('location', location)
  19. provide('geolocation', geolocation)
  20. }
  21. }
  22. </script>

现在,如果这两个 property 中有任何更改,MyMarker 组件也将自动更新!

修改响应式 property

当使用响应式提供/注入值时,建议尽可能,在提供者内保持响应式 property 的任何更改

例如,在需要更改用户位置的情况下,我们最好在 MyMap 组件中执行此操作。

  1. <!-- src/components/MyMap.vue -->
  2. <template>
  3. <MyMarker />
  4. </template>
  5. <script>
  6. import { provide, reactive, ref } from 'vue'
  7. import MyMarker from './MyMarker.vue
  8. export default {
  9. components: {
  10. MyMarker
  11. },
  12. setup() {
  13. const location = ref('North Pole')
  14. const geolocation = reactive({
  15. longitude: 90,
  16. latitude: 135
  17. })
  18. provide('location', location)
  19. provide('geolocation', geolocation)
  20. return {
  21. location
  22. }
  23. },
  24. methods: {
  25. updateLocation() {
  26. this.location = 'South Pole'
  27. }
  28. }
  29. }
  30. </script>

然而,有时我们需要在注入数据的组件内部更新注入的数据。在这种情况下,我们建议提供一个方法来负责改变响应式 property。

  1. <!-- src/components/MyMap.vue -->
  2. <template>
  3. <MyMarker />
  4. </template>
  5. <script>
  6. import { provide, reactive, ref } from 'vue'
  7. import MyMarker from './MyMarker.vue
  8. export default {
  9. components: {
  10. MyMarker
  11. },
  12. setup() {
  13. const location = ref('North Pole')
  14. const geolocation = reactive({
  15. longitude: 90,
  16. latitude: 135
  17. })
  18. const updateLocation = () => {
  19. location.value = 'South Pole'
  20. }
  21. provide('location', location)
  22. provide('geolocation', geolocation)
  23. provide('updateLocation', updateLocation)
  24. }
  25. }
  26. </script>
  1. <!-- src/components/MyMarker.vue -->
  2. <script>
  3. import { inject } from 'vue'
  4. export default {
  5. setup() {
  6. const userLocation = inject('location', 'The Universe')
  7. const userGeolocation = inject('geolocation')
  8. const updateUserLocation = inject('updateLocation')
  9. return {
  10. userLocation,
  11. userGeolocation,
  12. updateUserLocation
  13. }
  14. }
  15. }
  16. </script>

最后,如果要确保通过 provide 传递的数据不会被注入的组件更改,我们建议对提供者的 property 使用 readonly

  1. <!-- src/components/MyMap.vue -->
  2. <template>
  3. <MyMarker />
  4. </template>
  5. <script>
  6. import { provide, reactive, readonly, ref } from 'vue'
  7. import MyMarker from './MyMarker.vue
  8. export default {
  9. components: {
  10. MyMarker
  11. },
  12. setup() {
  13. const location = ref('North Pole')
  14. const geolocation = reactive({
  15. longitude: 90,
  16. latitude: 135
  17. })
  18. const updateLocation = () => {
  19. location.value = 'South Pole'
  20. }
  21. provide('location', readonly(location))
  22. provide('geolocation', readonly(geolocation))
  23. provide('updateLocation', updateLocation)
  24. }
  25. }
  26. </script>