当前位置:   article > 正文

Vue Provide / Inject 详细介绍(跨组件通信、响应式变化、版本变化)

vue provide

背景

通常,当我们需要从父组件向子组件传递数据时,我们使用 props。想象一下这样的结构:有一些深度嵌套的组件,而深层的子组件只需要父组件的部分内容。在这种情况下,如果仍然将 prop 沿着组件链逐级传递下去,可能会很麻烦。

对于这种情况,我们可以使用一对 provide 和 inject。无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。这个特性有两个部分:父组件有一个 provide 选项来提供数据,子组件有一个 inject 选项来开始使用这些数据。

Provide/inject scheme

使用

假设有一个组件A,A组件引入B组件(A为B的父组件) ,B组件引入C组件(B为C的父组件),即A为C的祖先组件,此时二者可以使用provide / inject进行通信。

举例

A.vue

  1. <template>
  2. <div>
  3. <B></B>
  4. </div>
  5. </template>
  6. <script>
  7. import B from "./B.vue";
  8. export default {
  9. name: "A",
  10. components: {
  11. B
  12. },
  13. };
  14. </script>

B.vue

  1. <template>
  2. <div>
  3. <C></C>
  4. </div>
  5. </template>
  6. <script>
  7. import C from "./C.vue";
  8. export default {
  9. name: "B",
  10. components: {
  11. C
  12. },
  13. };
  14. </script>

C.vue

  1. <template>
  2. <div>
  3. </div>
  4. </template>
  5. <script>
  6. export default {
  7. name: "C"
  8. };
  9. </script>

A与C使用provide / inject方式进行通信

A使用provide

  1. <template>
  2. <div>
  3. <B></B>
  4. </div>
  5. </template>
  6. <script>
  7. import B from "./B.vue";
  8. export default {
  9. name: "A",
  10. components: {
  11. B
  12. },
  13. provide:{
  14. name:"leo"
  15. }
  16. };
  17. </script>

C使用inject

  1. <template>
  2. <div>
  3. <span>{{name}}</span>
  4. </div>
  5. </template>
  6. <script>
  7. export default {
  8. name: "C",
  9. inject:["name"]
  10. };
  11. </script>

此时,C已经拿到A中的对应的name。但是,我们可能希望:当A中的name是本身某个可变化的数据时,如下:

  1. <template>
  2. <div>
  3. <B></B>
  4. </div>
  5. </template>
  6. <script>
  7. import B from "./B.vue";
  8. export default {
  9. name: "A",
  10. components: {
  11. B
  12. },
  13. provide:{
  14. name:this.name
  15. },
  16. data(){
  17. return {
  18. name:"leo"
  19. }
  20. },
  21. methods:{
  22. changeName(){
  23. this.name = "lion"
  24. }
  25. }
  26. };
  27. </script>

我们希望当name改变时(如触发changeName方法),对应的C中的name也要相应改变,但是使用以上方式时,C中的name并未随着改变,此时需要我们进一步处理,即处理响应性。

处理响应性 

在上面的例子中,如果我们更改了name,这个变化并不会反映在 inject 的 name property 中。这是因为默认情况下,provide/inject 绑定并不是响应式的。在vue3中,我们可以通过传递一个 ref property 或 reactive 对象给 provide 来改变这种行为(下面展开)。在我们的例子(vue2)中,如果我们想对祖先组件中的更改做出响应,我们需要将 provide 传值进行改变。

A使用provide,此时传入的应是一个响应式对象(如以下的obj)

  1. <template>
  2. <div>
  3. <B></B>
  4. </div>
  5. </template>
  6. <script>
  7. import B from "./B.vue";
  8. export default {
  9. name: "A",
  10. components: {
  11. B
  12. },
  13. provide(){
  14. return {
  15. obj:this.obj //传入一个响应式对象
  16. }
  17. },
  18. data(){
  19. return {
  20. obj:{
  21. name:"leo"
  22. }
  23. }
  24. },
  25. methods:{
  26. changeName(){
  27. this.obj.name = "lion"
  28. }
  29. }
  30. };
  31. </script>

C使用inject

  1. <template>
  2. <div>
  3. <span>{{obj.name}}</span>
  4. </div>
  5. </template>
  6. <script>
  7. export default {
  8. name: "C",
  9. inject:["obj"] //接收响应式对象
  10. };
  11. </script>

此时A中的name改变,C中的值也会相应跟着变化。

以上为A向C传数据,如果C向A传数据(或者说C需要改变A中的数据),该如何做?

我们这里不让C直接改变A中的数据,而是将A改变数据的方法通过provide传给C,C执行该方法,触发改变A中的数据。

A使用provide传入一个方法

  1. <template>
  2. <div>
  3. <span>{{obj.name}}</span>
  4. <B></B>
  5. </div>
  6. </template>
  7. <script>
  8. import B from "./B.vue";
  9. export default {
  10. name: "A",
  11. components: {
  12. B
  13. },
  14. provide(){
  15. return {
  16. changeVal:this.changeName //传入一个方法
  17. }
  18. },
  19. data(){
  20. return {
  21. obj:{
  22. name:"leo"
  23. }
  24. }
  25. },
  26. methods:{
  27. changeName(val){ //C中触发该方法执行,此时变成"lion"
  28. this.obj.name = val
  29. }
  30. }
  31. };
  32. </script>

C使用inject

  1. <template>
  2. <div>
  3. <span @click="changeName">点击改变A组件数据</span>
  4. </div>
  5. </template>
  6. <script>
  7. export default {
  8. name: "C",
  9. inject:["changeVal"], //接收一个方法
  10. methods:{
  11. changeName(){
  12. this.changeVal("lion") //执行此方法,改变A中的数据
  13. }
  14. }
  15. };
  16. </script>

以上就是在vue2中对provide / inject的基本使用。

vue3中使用

Provide

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

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

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

使用A组件,provide 的值可以按如下方式重构:

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

Inject

在 setup() 中使用 inject 时,也需要从 vue 显式导入。导入以后,我们就可以调用它来定义暴露给我们的组件方式。

inject 函数有两个参数:

  1. 要 inject 的 property 的 name
  2. 默认值 (可选)

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

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

上面提到,在vue3中处理响应性的方式

为了增加 provide 值和 inject 值之间的响应性,我们可以在 provide 值时使用 ref 或 reactive

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

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

同样的,需要在C中修改它的祖先组件的数据,需像vue2一样在provide传入一个方法

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

C中使用inject

  1. <script>
  2. import { inject } from 'vue'
  3. export default {
  4. setup() {
  5. const userLocation = inject('location', 'The Universe')
  6. const userGeolocation = inject('geolocation')
  7. const updateUserLocation = inject('updateLocation')
  8. return {
  9. userLocation,
  10. userGeolocation,
  11. updateUserLocation //执行该方法,触发祖先组件方法执行,从而改变数据
  12. }
  13. }
  14. }
  15. </script>

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

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

总结:主要介绍了provide / inject 的基本使用以及在vue2、vue3中使用的区别。 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/694083
推荐阅读
相关标签
  

闽ICP备14008679号