Recommended PC viewing, mobile code highlighting disorder

What happened to New Vue

Let’s walk through the core definition of Vue, using Web-full-ESM as an example

// scripts/config.js

const builds = {

  // ...

  

  // Runtime+compiler ES modules build (for bundlers)

  'web-full-esm': {

    entry: resolve('web/entry-runtime-with-compiler.js').

    dest: resolve('dist/vue.esm.js').

    format: 'es'.

    alias: { he: './entity-decoder' }.

    banner

  }.

}Copy the code
  • src/platforms/web/entry-runtime-with-compiler.js(entry)
  • — – >src/platforms/web/runtime/index.js
  • — – >src/core/index.js
  • — – >src/core/instance/index.js(core)

Vue constructor defined in SRC/core/instance/index, js

// src/core/instance/index.js

function Vue (options) {

  if (process.env.NODE_ENV ! = = 'production' &&

    !(this instanceof Vue)

  ) {

    warn('Vue is a constructor and should be called with the `new` keyword')

  }

  this._init(options)

}



initMixin(Vue)

stateMixin(Vue)

eventsMixin(Vue)

lifecycleMixin(Vue)

renderMixin(Vue)



export default VueCopy the code
  • initMixin(Vue): Does one thing, definition_initMethods.
  • stateMixin(Vue): Defines data related methods$set.$delete.$watchMethods.
  • eventsMixin(Vue): Defines event-related methods$on.$once.$off.$emit.
  • lifecycleMixin(Vue)Definition:_update, and life cycle related$forceUpdateand$destroy.
  • renderMixin(Vue)Definition:$nextTick._renderwillrenderFunction tovnode.

You can see that new Vue basically calls the _init method

_init method

Enclosing _init method when performing initMixin binding, in SRC/core/instance/init. In js:

export function initMixin (Vue) {

  Vue.prototype._init = function (options) {

    const vm = this

    // uid

    vm._uid = uid++



    // a flag to avoid this being observed

    vm._isVue = true



    if (options && options._isComponent) {

      // Optimize internal component instantiation because dynamic option merging is very slow and no internal component options require special handling.

      initInternalComponent(vm. options)

    } else {

      / / merge options

      vm.$options = mergeOptions(

        resolveConstructorOptions(vm.constructor).

        options || {}.

        vm

      )

    }

    

    if (process.env.NODE_ENV ! = = 'production') {

      initProxy(vm)

    } else {

      vm._renderProxy = vm

    }



    // expose real self

    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')



    / / a mount

    if (vm.$options.el) {

      vm.$mount(vm.$options.el)

    }

  }

}Copy the code

Vue initialization mainly does a few things:

  • mergeOptionsMerge configuration
  • initLifecycleInitialize the life cycle
  • initEventsInitialize the event center
  • initRenderInitialize render, such as definitionvm._cvm.$createElement
  • initStateInitialize thedata,props,computed,watcherAnd so on.

All of this will be covered in subsequent chapters, but here you just need to know what you did.

Data brokers

Have you ever wondered why our data is defined in data but can be accessed through this.xxx

export default {
  data() {
    return {
      name: 'l1shu'}},mounted() {
    console.log(this.name) // l1shu
  }
}
Copy the code

One of the mysteries in _init initState, SRC/core/instance/state. The js:

// src/core/instance/state.js

export function initState (vm) {

  // ...

  

  if (opts.data) {

    initData(vm) // Execute the initData method

  } else {

    observe(vm._data = {}. true /* asRootData */)

  }

  

  // ...

}Copy the code

Read data from $options and execute initData:

// src/core/instance/state.js

function initData (vm) {

  let data = vm.$options.data

  data = vm._data = typeof data = = = 'function'

    ? getData(data. vm)

    : data || {}

  if (!isPlainObject(data)) {

    data = {}

    process.env.NODE_ENV ! = = 'production' && warn(

      'data functions should return an object:\n' +

      'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function'.

      vm

    )

  }

  // proxy data on instance

  const keys = Object.keys(data)

  const props = vm.$options.props

  const methods = vm.$options.methods

  let i = keys.length

  while (i--) {

    const key = keys[i]

    if (process.env.NODE_ENV ! = = 'production') {

      // Check whether it conflicts with methods

      if (methods && hasOwn(methods. key)) {

        warn(

          `Method "The ${key}" has already been defined as a data property.`.

          vm

        )

      }

    }

    // check if the same name conflicts with props

    if (props && hasOwn(props. key)) {

      process.env.NODE_ENV ! = = 'production' && warn(

        `The data property "The ${key}" is already declared as a prop.` +

        `Use prop default value instead.`.

        vm

      )

    } else if (!isReserved(key)) {

      // Data broker

      proxy(vm. `_data`. key)

    }

  }

  // Reactive processing

  observe(data. true /* asRootData */)

}Copy the code

First check whether data is a function, then execute getData, which is defined as follows:

export function getData (data: Function. vm: Component): any {

  // #7573 disable dep collection when invoking data getters

  // https://github.com/vuejs/vue/issues/7573

  pushTarget()

  try {

    return data.call(vm. vm)

  } catch (e) {

    handleError(e. vm. `data()`)

    return {}

  } finally {

    popTarget()

  }

}Copy the code

As you can see, the getData function logic is very simple, and pushTarget and popTarget will be covered in a later section on responsiveness.

Back to the initData function:

  • traversekeysTo determine whether the defined field andprops.methodsConflict of the same name, and a warning is thrown in development mode
  • isReservedCheck whether the field is reserved_or$At the beginning
  • proxyImplementing the data broker (Core)
  • observeImplementing reactive processing, which we’ll talk about later

Finally, see how the proxy function implements data proxy:

// src/core/instance/state.js

const sharedPropertyDefinition = {

  enumerable: true.

  configurable: true.

  get: noop.

  set: noop

}



export function proxy (target: Object. sourceKey: string. key: string) {

  sharedPropertyDefinition.get = function proxyGetter () {

    return this[sourceKey][key]

  }

  sharedPropertyDefinition.set = function proxySetter (val) {

    this[sourceKey][key] = val

  }

  Object.defineProperty(target. key. sharedPropertyDefinition)

}Copy the code

Add the specified key to the VM using Object.defineProperty, and modify or read this._data using the set and GET methods.

When we call this.message, we actually call this._data.message

conclusion

Vue initialization logic written very clear, the different function logic into a number of separate functions to execute, so that the main logic at a glance

Since our goal in this chapter is to figure out how the template and data are rendered into the final DOM, we’ll leave the initialization logic behind.

At the end of initialization, we detect that if there is an EL attribute, we call the vm.$mount method to mount the VM. The goal of mount is to render the template into the final DOM, so let’s analyze the mount process of Vue.