10

provide/inject basic usage

In Vue.js , if a cross-level component wants to pass data, we can directly use props to pass the data of the ancestor component to the descendant component:

prop-drilling.png

Note: The picture above is from Vue.js official website: Prop Drilling .

As shown above, the intermediate component <Footer> may not need this part at all props , but in order to <Footer> <DeepChiild> can access these props , <Footer> still need to define these props and pass it on.

Some people say that we can use $attrs/$listeners , but we still have to go through the middle level, and using Vuex is too troublesome, and Event Bus can easily lead to logical dispersion and problems difficult to locate.

So, is there any other way to pass data directly from ancestor component to descendant component? The answer is provide/inject .

Ancestor component:

 // Root.vue

<script setup>
import { provide } from 'vue'

provide('msg' /* 注入的键名 */ , 'Vue.js' /* 值 */)
</script>

Descendant components:

 // DeepChild.vue

<script setup>
import { inject } from 'vue'
  
const msg = inject('msg' /* 注入的键名 */, 'World' /* 默认值 */)
</script>

For specific usage, see: Provide / Inject .

Now, the problem is solved:

prop-drilling2.png

Note: The above picture is from Vue.js official website: Prop Drilling .

provide implementation principle

How is such a miraculous thing achieved?

 export function provide<T>(key: InjectionKey<T> | string | number, value: T) {
  let provides = currentInstance.provides

  const parentProvides = currentInstance.parent && currentInstance.parent.provides
  if (parentProvides === provides) {
    provides = currentInstance.provides = Object.create(parentProvides)
  }
  
  provides[key as string] = value
}

By default, a component instance's provides inherit from its parent component. But when a component instance needs to provide its own value, it uses the parent component's provides object as a prototype to create its own provides object. In this way, when using inject , we can find the data provided by the parent component through the prototype chain .

inject implementation principle

The code of inject is also very simple, so simple that you will come to a sentence after reading it:

就这?

 export function inject(
  key: InjectionKey<any> | string,
  defaultValue?: unknown,
  treatDefaultAsFactory = false
) {
  const instance = currentInstance || currentRenderingInstance

  if (instance) {
    // #2400
    // to support `app.use` plugins,
    // fallback to appContext's `provides` if the instance is at root
    const provides = instance.parent == null
        ? instance.vnode.appContext && instance.vnode.appContext.provides
        : instance.parent.provides

    if (provides && (key as string | symbol) in provides) {
      return provides[key as string]
    } else if (arguments.length > 1) {
      return treatDefaultAsFactory && isFunction(defaultValue)
        ? defaultValue.call(instance.proxy)
        : defaultValue
    }
  }
}

inject has two main functions:

  • Through the in operation to obtain the data of the parent component, the in operation will traverse the prototype chain, which is the implementation of the above provide , why the component uses the provides of the parent component Objects are used as prototypes to create their own provides objects
  • Implement the default value function of inject 1996bba74102f3d5193d33fd230f04d4---, inject The second parameter is the default value

One sentence summary: provide/inject Use the prototype chain to realize data transfer across hierarchical components.


Seng
121 声望552 粉丝

主业:Web 前端开发,兴趣爱好:读书、爬山