The previous article studied vue source code (17) to explore the lifecycle of the initialization instance properties and events explained the initialization phase

    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
Copy the code

The beforeCreate hook function initializes instance properties and events before firing.

  // src/core/instance/init.js
  Vue.prototype._init = function (options? : Object) {
...    vm._self = vm
    initLifecycle(vm)
 initEvents(vm)  initRender(vm)  callHook(vm, 'beforeCreate')  initInjections(vm) // resolve injections before data/props  initState(vm)  initProvide(vm) // resolve provide after data/props  callHook(vm, 'created')  } Copy the code

InitProvide beforeCreate is the trigger for initState and initProvide, so be afraid to use initState injections when using the created hook function. I’ll do it in a second.

Concept of analytical

Pairs: Provide and inject come in pairs

Purpose: Used by a parent component to pass data to descendant components

Provide returns data to children in parent components, inject data into children or grandchildren that need to use this data.

Usage scenario: Since Vue has the $parent attribute, it allows child components to access parent components. However, it is more difficult for a grandchild component to access its ancestor component. Provide/Inject enables easy cross-level access to parent component data

use

Provide and Inject are used together as communication modes for multi-layer components. I’m sure this will come up when the interview asks how vUE components communicate with each other. (what? You said you didn’t know… , that’s good to know, next time will have to say)


To put it simply, when there are too many levels of component introduction, and our descendant component wants to get resources from the ancestor component, what can we do? We can’t always go up the parent level, and the code structure is easily confused. That’s what this pair of choices is going to do

Provide and inject need to be used together. Their meanings are as follows:

Provide: an object or function that returns an object containing properties that can be injected into posterity using ES6 Symbols as the key(only Symbols can be used natively)

Inject: The Inject option should be an array of strings or an object whose key represents the name of the local binding and whose value is its key (string or Symbol) to search in available injections.

Let me give you an example


          
<html lang="en"> 
<head>
    <meta charset="UTF-8">
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
 <title>Document</title> </head> <body>  <div id="app"><child></child></div>  <script>  Vue.component('child', { inject: ['message']. template:'<p>{{message}}</p>'  })  new Vue({  el:'#app'. provide: {message:'Hello Vue! '}  })  </script> </body> </html> Copy the code

Output: Hello Vue! , the corresponding DOM node is rendered as:


It is similar to the function of props. Provide /inject a layer of components.


       
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
 <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script> </head> <body>  <div id="app"><test></test></div>  <script>  Vue.component('child', { inject: ['message']. template:'<p>{{message}}</p>'  })  Vue.component('test', { template:`<div><child></child></div>`  })  new Vue({  el:'#app'. provide: {message:'Hello Vue! '}  })  </script> </body> </html> Copy the code

Provide /injec is used for this purpose, not too cool when using multiple layers of nesting

Source code analysis

provide

// src/core/instance/inject.js
export function initProvide (vm: Component) {
  const provide = vm.$options.provide
  if (provide) {
    vm._provided = typeof provide === 'function'
 ? provide.call(vm)  : provide  } } Copy the code

Provide is the option to pass data down. Here we get the provide option. If there is a provide option, we pass the provide option to the vm._provided into Vue instance global data.

It is clearer to look at the example below, where the data foo is passed and the data content is bar.

var Provider = {
  provide: {
    foo: 'bar'
  },
  // ...
} Copy the code

inject

// src/core/instance/inject.js
export function initInjections (vm: Component) {
  const result = resolveInject(vm.$options.inject, vm)
  if (result) {
    observerState.shouldConvert = false
 Object.keys(result).forEach(key= > {  defineReactive(vm, key, result[key])  })  observerState.shouldConvert = true  } } Copy the code

As can be seen from the simplified source code, first obtain the inject option search result through the resolveInject method. If there is a search result, iterate over the search result and add setters and getters for the data therein. Let’s look at the resolveInject method:

export function resolveInject (inject: any, vm: Component): ?Object {
  if (inject) {
    const result = Object.create(null)
    // Get the key array of inject options
    const keys = hasSymbol
 ? Reflect.ownKeys(inject).filter(key= > {  /* istanbul ignore next */  return Object.getOwnPropertyDescriptor(inject, key).enumerable  })  : Object.keys(inject)   for (let i = 0; i < keys.length; i++) {  const key = keys[i]  const provideKey = inject[key].from  let source = vm  while (source) {  if (source._provided && provideKey in source._provided) {  result[key] = source._provided[provideKey]  break  }  source = source.$parent  }  if(! source) { if ('default' in inject[key]) {  const provideDefault = inject[key].default  result[key] = typeof provideDefault === 'function'  ? provideDefault.call(vm)  : provideDefault  } else if(process.env.NODE_ENV ! = ='production') {  warn(`Injection "${key}" not found`, vm)  }  }  }  return result  } } Copy the code

Gets the key array of inject options

Iterate through the key array to find if there is a key in provide with the same name as the From attribute in the Inject option by bubbling up

If so, this data is passed to result;

If not, check whether inject has the default option to set the default value or the default method. If yes, the default value is passed back to Result and the result object is returned.

Inject value default: Inject value default

const Child = {
  inject: {
    foo: { default: 'foo' }
  }
}
Copy the code

Or have the from lookup key and the default value:

const Child = {
  inject: {
    foo: {
      from: 'bar'.      default: 'foo'
 }  } } Copy the code

Or set a factory method for the default value:

const Child = {
  inject: {
    foo: {
      from: 'bar'.      default: (a)= > [1.2.3]
 }  } } Copy the code

Ok, I admit that these are the three examples cited on the website, but there you go.

This article is formatted using MDNICE