Analyze the use method and principle of provide and inject of vue

  • 2021-12-04 09:09:32
  • OfStack

First of all, let's talk about why we use provide/inject. For communication between grandpa and grandson components, or even between grandpa and grandson components, we use vuex instead of ok.

That's true, but, please listen to me, sometimes your project is small and even there are few scenarios for component communication, so it is wasteful for you to introduce vuex just for a few communication parameters. Some people may also think of using $parent Get the parent component instance to get data/methods. This two-tier is fine. What about multi-tier components? If the components are nested deeply, what do you do? Write a function to put $parent Package 1 again. Isn't that very troublesome? You don't have to save the nation with curves. Haha ~ digress.

Don't talk so much nonsense, just tell you that using provide/inject is to solve your problems, which is absolutely true. Let's see how to use it. A backhand is just a few simple lines of code:


1. The parent component provides parameters to be passed to the child component 
provide() {
    return {
      listType: this.listType,
    }
  }
2. Subcomponents use: 
inject: ['listType'],

Of course, you can also specify your default values and the source of your parameters in inject:


inject:{
  listType:{
  from:"par"//provide The name of the definition 
  default:1
  }
}

All right! Isn't it very simple? In fact, both parent and ancestor components can inject dependencies into descendant components, no matter how deep the component is.

Say one more:

provide can be either an object or a function that returns an object.

inejct: It can be an array of strings or an object.

If you are interested, look at the following source code section, which is also quite easy to understand:

provide core source code:


export function provide<T>(key: InjectionKey<T> | string | number, value: T) {
  if (!currentInstance) {
    if (__DEV__) {
      warn(`provide() can only be used inside setup().`)
    
    }
  } else {
    // Object of the current component provides, The default instance inherits the parent class's provides Object 
    let provides = currentInstance.provides
    // Use parent provide Object as a prototype to create your own provide Object 
    const parentProvides =
      currentInstance.parent && currentInstance.parent.provides
    if (parentProvides === provides) {
      provides = currentInstance.provides = Object.create(parentProvides)
    }
    provides[key as string] = value
  }
}
​

inject core source code:


export function inject(
  key: InjectionKey<any> | string,
  defaultValue?: unknown,
  treatDefaultAsFactory = false
) {
  // Gets the current component instance 
  const instance = currentInstance || currentRenderingInstance
  if (instance) {
  // Get provides
    const provides =
      instance.parent == null
        ? instance.vnode.appContext && instance.vnode.appContext.provides
        : instance.parent.provides
​
    if (provides && (key as string | symbol) in provides) {
      // If key If it exists, it will return directly 
      return provides[key as string]
    } else if (arguments.length > 1) {
      // If key Does not exist, if the default value is set, the default value will be returned directly 
      return treatDefaultAsFactory && isFunction(defaultValue)
        ? defaultValue.call(instance.proxy)
        : defaultValue
    } else if (__DEV__) {
      // If there is none, prompt 
      warn(`injection "${String(key)}" not found.`)
    }
  } else if (__DEV__) {
    warn(`inject() can only be used inside setup() or functional components.`)
  }
}
​

Related articles: