Last time we looked at some of the operations before the new Vue() beforeCreate life cycle in general, this time we’ll look at what happens between beforeCreate and Created

 initInjections(vm); // resolve injections before data/props
 initState(vm);
 initProvide(vm); // resolve provide after data/props
Copy the code

Vue2.0 source code -initMixin (2)

initInjections(vm)

This pair of options needs to be used together to allow an ancestor component to inject a dependency into all of its descendants. No matter how deep the component hierarchy is, it remains in effect for as long as its upstream and downstream relationships are established.

export function initInjections (vm: Component) { const result = resolveInject(vm.$options.inject, vm) if (result) { toggleObserving(false) Object.keys(result).forEach(key => { /* istanbul ignore else */ if (process.env.NODE_ENV ! == 'production') { defineReactive(vm, key, result[key], () => { warn( `Avoid mutating an injected value directly since the changes will be ` + `overwritten whenever the provided component re-renders. ` + `injection being mutated: "${key}"`, vm ) }) } else { defineReactive(vm, key, result[key]) } }) toggleObserving(true) } }Copy the code
export function resolveInject (inject: any, vm: Component): ? Object { if (inject) { // inject is :any because flow is not smart enough to figure out cached const result = Object.create(null) const keys = hasSymbol ? Reflect.ownKeys(inject) : Object.keys(inject) for (let i = 0; i < keys.length; i++) { const key = keys[i] // #6574 in case the inject object is observed... if (key === '__ob__') continue const provideKey = inject[key].from let source = vm while (source) { if (source._provided  && hasOwn(source._provided, provideKey)) { 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

The resolveInject method

const result = resolveInject(vm.$options.inject, vm)
Copy the code

We can write a inject and then check the value of resolveInject by breakpoint

NormalizeInject array [‘ MSG ‘] is converted into an object by normalizeInject when mergeOptions is called at the beginning of the _init method. We’ll talk about that later.

  • Object.create(null) is used to create an empty Object modeled after NULL
  • HasSymbol is used to determine whether the developer’s current environment supports Symbol and reflect. ownKeys, and if it does, uses reflect. ownKeys to retrieve an array of values based on the current object’s key. Otherwise, use object. keys.
const result = Object.create(null)
const keys = hasSymbol
      ? Reflect.ownKeys(inject)
      : Object.keys(inject)
Copy the code

Next, let’s look at the operations inside the for loop

If key===ob, stop this loop and execute the next one. In this case, the first effect to be achieved is to skip key __ob__.

if (key === "__ob__") continue;
Copy the code
  • If found, it terminates the loop and assigns the value to Result. Otherwise, it continues to the parent and the parent of the parent until it reaches the root component.
  • And init provided’s operation, initProvide, is after initInjections, Parent beforeCreate-> Parent created-> parent beforeMount-> child beforeCreate-> child created. The parent’s initProvide is already initialized, but its own is not. So its own source._provided is undefined and does not validate the component’s own provide.
 const provideKey = inject[key].from;
      let source = vm;
      while (source) {
        if (source._provided && hasOwn(source._provided, provideKey)) {
          result[key] = source._provided[provideKey];
          break;
       }
     source = source.$parent;
   }
Copy the code

If source [key] is the parent of root component, undefined and provide is not found, inject[key] will be assigned to result[key] as default. Otherwise, there is no corresponding parameter or default value, and an error warning is reported.

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); }}Copy the code

ToggleObserving is used to toggle whether the value registered by defineReactive is responsive, that is, when the value sent by the parent component provides changes. The value accepted by Inject does not change. Unless the value injected accepts is itself responsive.

 toggleObserving(false);
    defineReactive(vm, key, result[key])
 toggleObserving(true);
Copy the code

initState(vm)

InitState see the name, I can guess, this is used to give some of the properties for assignment operation, props, the methods, data, computed, watch these attributes.

export function initState (vm: Component) { vm._watchers = [] const opts = vm.$options if (opts.props) initProps(vm, opts.props) if (opts.methods) initMethods(vm, opts.methods) if (opts.data) { initData(vm) } else { observe(vm._data = {}, true /* asRootData */) } if (opts.computed) initComputed(vm, opts.computed) if (opts.watch && opts.watch ! == nativeWatch) { initWatch(vm, opts.watch) } }Copy the code

initProps

For the assignment and initialization of vm._props operations, there is too much source code to post at once. The following code is the main operation

  • The vm._props object is defined
  • vm.$options._propKeys
  • IsRoot is defined to determine whether the component isRoot
const propsData = vm.$options.propsData || {} const props = vm._props = {} // cache prop keys so that future props updates can iterate using Array // instead of dynamic object key enumeration. const keys = vm.$options._propKeys = [] const isRoot = ! vm.$parentCopy the code
  • And then we look at the loop down here
  • Turn off responsiveness when the component is not the root component.
  • Check whether it is a development environment
    • We first convert our key with hyphenate method and cache operation with closure. The main function of this method is to convert the key we pass in, such as DemoD to demo-d.
    • IsReservedAttribute is used to determine whether the key you are using is keywords such as key,ref,slot,slot-scope,is. If so, raise a red flag.
    • Config. IsReservedAttr is always false.
    • Then call defineReactive to override the attribute’s set and get methods.
  • In production, the defineReactive method is called directly
  • Finally, check whether this attribute exists on this object. If not, assign the attribute to this and rewrite set. Connect the get method with _props to make it easier to use this. Use it directly.
if (! isRoot) { toggleObserving(false) } for (const key in propsOptions) { keys.push(key) const value = validateProp(key, propsOptions, propsData, vm) if (process.env.NODE_ENV ! == 'production') { const hyphenatedKey = hyphenate(key) if (isReservedAttribute(hyphenatedKey) || config.isReservedAttr(hyphenatedKey)) { warn( `"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`, vm ) } defineReactive(props, key, value, () => { if (! isRoot && ! isUpdatingChildComponent) { warn( `Avoid mutating a prop directly since the value will be ` + `overwritten whenever the parent component re-renders. ` + `Instead, use a data or computed property based on the prop's ` + `value. Prop being mutated: "${key}"`, vm ) } }) } else { defineReactive(props, key, value) } // static props are already proxied on the component's prototype // during Vue.extend(). We only need to proxy props defined at // instantiation here. if (! (key in vm)) { proxy(vm, `_props`, key) } }Copy the code

initMethods

InitMethods is used to initialize the methods that we write inside methods. Let’s just look at the source code, this is a little bit easier.

  • Obtain props from options to determine whether there is naming duplication between props and methods.
  • Check that methods[key] is a method, and that the key name cannot be defined repeatedly, and cannot start with $or _.
  • Finally, define the method directly on this, and change the this in the method to point to VM.
function initMethods(vm: Component, methods: Object) { const props = vm.$options.props; for (const key in methods) { if (process.env.NODE_ENV ! == "production") { if (typeof methods[key] ! == "function") { warn( `Method "${key}" has type "${typeof methods[ key ]}" in the component definition. ` + `Did you reference the function correctly? `, vm ); } if (props && hasOwn(props, key)) { warn(`Method "${key}" has already been defined as a prop.`, vm); } if (key in vm && isReserved(key)) { warn( `Method "${key}" conflicts with an existing Vue instance method. ` + `Avoid defining component methods that start with _ or $.` ); } } vm[key] = typeof methods[key] ! == "function" ? noop : bind(methods[key], vm); }}Copy the code

After initData, initComputed, and initWatch, we will analyze the implementation principle separately.

initProvide(vm)

export function initProvide(vm: Component) { const provide = vm.$options.provide; if (provide) { vm._provided = typeof provide === "function" ? provide.call(vm) : provide; }}Copy the code

InitProvide is simple, just a simple assignment that assigns the provide we wrote to the _provided property of the VM and changes its this to the VM if the provide passed in is a function, otherwise it assigns directly.

That’s enough for an overview of initMixin, and we’ll take a closer look at what it does to merge $options, how data is responsive, computed, and watch are implemented.