Vue initialization beforeCreate before doing what?

Let’s continue with the initialization of this._init(), which then performs three initialization methods like this:

initInjections(vm)
initState(vm)
initProvide(vm)
Copy the code

5. InitInjections (VM): the main function is to initialize inject and access the corresponding dependency.

Inject and provide. This is a pair of apis added in [email protected] that need to be used together. It allows the parent component to provide dependencies to all descendant components after it, so that descendant components can be accessed no matter how deep the nesting

  • provide: Provides an object or a function that returns an object.
  • inject: is an array or object of strings.

This pair of APIS on the official website of vue has two edible tips:

Provide and Inject mainly provide use cases for high-level plug-in/component libraries. Direct use in application code is not recommended.

  • This is probably because it confuses the hierarchy of component data, but it works well when developing component libraries.

Provide and Inject binding are not responsive. This is intentional. However, if you pass in a listening object, the object’s properties are still responsive.

  • There is a trick here, which is to take the root componentdataProperties defined in the class are provided to descendant components, which are no longer usedvuexCan achieve simple global state management, or verycoolThe ~
App. Vue root componentexport default {
  provide() {
    return {
      app: this
    }
  },
  data() {
    return {
      info: 'hello world! '}}} Child.vue descendant componentexport default {
  inject: ['app'],
  methods: {
    handleClick() {
      this.app.info = 'hello vue! '}}}Copy the code

Once the handleClick event is triggered, any descendant component that uses the Inject this.app.info variable will be responded no matter how deep it is nested, and this completes simple VUex. More examples we can go to vUE’s official website to browse, here is not the code word, now we will analyze so cool function it is exactly how to achieve ~

Inject and provide are internally initialized separately, although they are used in pairs. As can be seen from the above three initialization methods, initialize inject, initialize props/data state correlation, and initialize provide. The purpose of this is to be able to use the injected content in props/data.

Let’s first look at the method definition when initializing inject:

export function initInjections(vm) {
  const result = resolveInject(vm.$options.inject, vm) // Find results... }Copy the code

Inject = vm.$options.inject = vm.$options.inject = vm.$options.inject = vm.

export function resolveInject (inject, vm) {
  if(inject) {const result = object.create (null) const keys = object.keys (inject) // omit Symbol casefor (let i = 0; i < keys.length; i++) {
      const key = keys[i]
      const provideKey = inject[key].from
      let source = vm
      while (source) {
        if(source._provided && hasOwn(source._provided, provideKey)) {//hasOwn specifies whether result[key] = source._provided[provideKey].break
        }
        source = source.$parent}... Add logic to set the default parameter of inject after [email protected]return result
  }
}
Copy the code

We first define a result to return the result we found. The outer for loop will iterate over every item of Inject, and then the inner layer uses the while loop to find whether the parent of inject item provides corresponding dependencies from bottom up.

Ps: there may be someone will doubt, inject before the definition of the array, i.e., how can here through Object. The keys and values? This is because when we do the options merge in the previous chapter, we will also format the parameters. For example, the format of props, as defined as an array, will also be converted to object format. Inject is defined like this:

{inject: ['app'} inject: {app: {from:'app'}}}Copy the code

Following the text, source is the current instance, and source._provided holds the value currently provided. It looks from the current instance, then assigns its parent component instance to the source, and looks at its parent component. Break out of the loop, assign the result of the search to result, and then look for the next one.

Ps: Some people may have some questions, this time, it initializes the inject and then initialize the provide, how to access the parent provide? The parent component is initialized first, and the child component is initialized second, so it has the source._provided property.

After finding the result we thought of, we complete the previous definition of initInjections:

export function initInjections(vm) {
  const result = resolveInject(vm.$options.inject, vm)

  if(result) {// If there are results toggleObserving(falseKeys (result). ForEach (key => {... defineReactive(vm, key, result[key]) }) toggleObserving(true)}}Copy the code

If there are search results, we will first call toggleObserving(false). We don’t care about the implementation, except that what this method does is set up a flag bit that will determine whether the defineReactive() method sets its third parameter to responsive data. If the value of result[key] is set to the value of responsiveData, the parameter is false, and the value of key is set to the normal value under the VM, but this can be used in the current instance to access the corresponding dependency in inject. Then call toggleObserving(true) and change the bit so that defineReactive() can set the third parameter to reactive data, which is what it should be. The above is the relevant principle of inject realization. In a word, it first iterates each item and then iterates one by one whether the parent of each item has dependencies.

6. InitState (VM): specifies the state to be used for initialization, including props, Methods, data, computed, and watch.

First look at the definition of the initState(VM) method:

export function initState(vm) {
  ...
  const opts = vm.$options
  if(opts.props) initProps(vm, opts.props)
  if(opts.methods) initMethods(vm, opts.methods)
  if(opts.data) initData(vm)
  ...
  if(opts.computed) initComputed(vm, opts.computed)
  if(opts.watch && opts.watch ! == nativeWatch) { initWatch(vm, opts.watch) } }Copy the code

For now, I’m just going to talk about what we do with the initialization of the first three types of states, namely props, methods, and data, because computed and watch involve the reactive dependent watcher, so I’m going to skip this. Here are the three initializers:

6.1 initProps (VM, propsOptions) :

  • The main function is to check whether the values accepted by the child components conform to the rules and make the corresponding values availablethisDirect access.
functionInitProps (vm, propsOptions) {// The second argument to the validation rule is const propsData = vm.$options. PropsData | | {} / / props specific value const props. = the vm _props = {} / / store props const isRoot =! vm.$parent// Is the root nodeif(! isRoot) { toggleObserving(false)}for (const key in propsOptions) {
    const value = validateProp(key, propsOptions, propsData, vm)
    defineReactive(props, key, value)
    if(! (keyin vm)) {
      proxy(vm, `_props`, key)
    }
  }
  toggleObserving(true)}Copy the code

We know that props is an important way for the parent component to communicate with its children, and that the second parameter in initProps, propsOptions, is the current instance, the child component in the communication role, which defines the rule to accept the parameters. The props rule for subcomponents can be defined as an array, but after merging options, it is formatted as an object:

When defining: {props: ['name'.'age'} name: {name: {type: null
  },
  age: {
    type: null
  }
}
Copy the code

So when defining the props rules, just use the object format, which is a better way to write the specification.

Now that you know the rules, the next thing you need to know is the exact value that the parent component passes to the child component, which is placed in vm.$options.propsData as an object, which is also what you get when you merge options. Next, we define an empty object under the instance, vm._props, to mount the values that meet the specification. IsRoot checks whether the current component isRoot or not. If not, it does not convert the props to responsive data.

Then, the validation rule of the formatted props is iterated through, and the validation rule is verified using the validateProp method to obtain the corresponding value, and the value is mounted to the VM._props. This._props can now access the props defined value:

props: ['name'],
methods: {
  handleClick() {
    console.log(this._props.name)
  }
}
Copy the code

It wasn’t friendly to access internal private variables directly, so vue did a layer of proxy inside to switch access to this.name to this._props. Proxy needs to be introduced here, because it will be used in data later. Let’s look at its definition:

Format it:export function proxy(target, sourceKey, key) {
  Object.defineProperty(target, key, {
    enumerable: true,
    configurable: true,
    get: function () {
      return this[sourceKey][key]
    },
    set: function () {
      this[sourceKey][key] = val
    }
  })
}
Copy the code

It is very simple, just define a get method for an object value, and when it reads, let it return another value. This completes the initialization of props.

6.2 initMethods (VM, methods) :

  • The main function is to changemethodsInside the method mount tothisUnder.
function initMethods(vm, methods) {
  const props = vm.$options.props
  for(const key in methods) {
    
    if(the methods [key] = = null) {/ / the methods [key] = = = null | | the methods [key] = = = undefined shorthand warn (` defines the key only and no corresponding value `)}if(props && hasOwn(props, key)) {warn(' method name is the same as the key of the props)}if((key inVm) &&isreserved (key)) {warn(' method name already exists and starts with _ or $')} vm[key] = methods[key] == null? Noop // empty function:bindBind (vm) {bind(vm)}}Copy the code

Initialization of methods is relatively simple. There are many boundary cases, such as defining only the key but not the method implementation, the name of the key and the props being the same, and the name of the key already existing and not properly named, starting with _ or $. We explained why this is not the case in chapter 1. Finally, mount methods within methods under this to complete the initialization of methods.

6.3 initData (vm) :

  • The primary function is initializationdataAgain, mount tothisUnder. There’s an important point. The reasondataThe data inside is reactive, it is initialized here, you have to remember that ~.
function initData (vm: Component) {
  let data = vm.$options.data
  data = vm._data = typeof data === 'function'? GetData (data, vm) / / through the data. The call (vm and vm) get the object returned: data | | {}if(! IsPlainObject (data)) {// If not an Object format data = {} warn(' data must be an Object ')} const keys = object. keys(data) const props = VM.$options.props // get props const methods = vm.$options.methods // Get methodslet i = keys.length
  while (i--) {
    const key = keys[i]
    if(methods && hasOwn(methods, key)) {warn(' same name as methods in methods')}if(props && hasOwn(props, key)) {warn(' same as key ')}else if(! IsReserved (key)) {// Key cannot start with _ or $proxy(VM, '_data', key)}} observe(data,true)}Copy the code

$options.data = function; otherwise, return data or {}, and assign the result to vm._data. This is the same routine as props, which is used to make a layer of proxies. If the result is not an object format or an error is reported.

Then we iterate through each item in data, which cannot have the same name as the key in methods and props. Then we use proxy to create a layer of proxies. Observe (data, true), which recursively makes every item in the data responsive.

In fact, it is not difficult to find that the three of them mainly do the same thing, first do not have the same name between each other, and then can be directly accessed by this.

7. InitProvide (VM): The primary role is to initialize provide dependencies for child components.

The provide option should be an object or a function, so it can be evaluated, just like the value in data.

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

$options.provide = vm. provide = vm. provide = vm. provide = vm. provide = vm. provide = vm. provide = vm. provide = vm. provide = vm. provide So the child component can access the dependency provided by the parent component when initializing inject; If it is not a function type, return the defined provide.

8. CallHook (VM, ‘created’): Executes user-defined created hook functions, including mixins.

The created hook function () function is created by creating a hook function ().

  • InitInjections (VM) : Make child componentsinjectCan access the correct value
  • InitState (VM) : Mounts the state of the component definition tothisUnder.
  • InitProvide (VM) : initializes those provided by the parent componentprovideRely on.
  • Created: implements the componentcreatedHook function

Now that the initialization phase is over, we’ll move on to the component mount phase. We’ll end this chapter with a typical vue interview question:

The interviewer smiled politely and asked,

  • Excuse me,methodsCan the arrow function be used in the method inside, and what will be the result?

Dui back:

  • You can’t use the arrow function because the arrow functionthisIt’s bound when it’s defined. invueThe internal,methodsThe context of each method within is currentvmComponent instance,methods[key].bind(vm)If the arrow function is used, the context of the function becomes the parent context, i.eundefinedYes, the result is a yesundefinedAccessing any variable will result in an error.

Do you know how the much-talked-about virtual Dom is generated? (on)

Easy to click a like or follow bai, also easy to find ~

Reference:

Vue. Js source code comprehensive in-depth analysis

Vue.js is easy to understand

Vue. Js components

Share a component library for everyone, may use up ~ ↓

A library of vUE functional components that you might want to use.