In the last article, we discussed new Vue({… }) what happens behind the scenes. So what happens when we do dom mount after we instantiate vue?

Careful students will notice that the $mount method is defined in multiple files, such as:

  • src/platform/web/entry-runtime-with-compiler.js
  • src/platform/web/runtime/index.js
  • src/platform/weex/runtime/index.js

The reason for this is that the $mount implementation is platform-dependent and build-dependent

$mount trunk code is as follows:

Vue.prototype.$mount = function(el? : string | Element, hydrating? : boolean) :Component {
  el = el && query(el)
  The // query method actually converts the el argument, which could be a string or element. If it's a string, it returns Document.querySelector (EL)
  // ...
  const options = this.$options

  if(! options.render) {// Render function does not exist
    let template = options.template
    
    if (template) {
       // If there is a template configuration item:
       // 1. Template might be "#xx", so get the element content by id
       // 2. If the template has a nodeType, get the template.innerHTML content
    }else {
       // If template does not exist, but el does:
      /* * for example: new Vue({* el: "#app", *... * * / *})
      // Get the corresponding element content according to el
    }

    // Call compileToFunctions with the template fetched as an argument
    // compileToFunctions will return the render function methods, which will be stored under vm.$options
    const{ render, staticRenderFns } = compileToFunctions(template, {... }) options.render = render }return mount.call(this, el, hydrating)
}
Copy the code

From the trunk code, we can see that the following things are done

  • Since the EL argument has two types, either String or Element, call the query method to convert it to element
  • If the render function is not written by hand, get the template content first. Template is called to compileToFunctions and the render function is returned.
  • Finally, call mount.call, which actually calls the runtime/index.js mount method

Note:

  1. Vue Compiler has two versions: one is the build-time version, that is, we use VUe-Loader + Webpack. The other version is the runtime version, which is resolved by the Compiler at runtime. We’re looking at runtime here
  2. Vue ultimately only recognizes the render function, so if we write the render function manually, we call mount.call directly. Instead, Vue takes template as an argument, calls compileToFunctions at run time, converts it to the render function, and calls mount.call.
  3. In the build-time version, vue-loader + webpack will first convert our native code into the render function, which will call mount.call directly. In production, we recommend the build time version. Personal learning recommends the runtime version.
  4. The mount.call method actually calls the $mount method under Runtime /index.js, which is quite simple and calls the mountComponent method.

MountComponent trunk code:

export function mountComponent(vm: Component, el: ? Element, hydrating? : boolean) :Component {
  // ...
  // Call the beforeMount lifecycle function
  callHook(vm, 'beforeMount')
  // ...
  // Define the updateComonent method
  let updateComponent = () = > {
      vm._update(vm._render(), hydrating)
  }
  
  // ...
  new Watcher(vm, updateComponent, noop, {
    before () {
      if(vm._isMounted && ! vm._isDestroyed) { callHook(vm,'beforeUpdate')}}},true)
  // ...
  // Call the life cycle function Mounted
  callHook(vm, 'mounted')}Copy the code

Watch class code

The Watch class has a lot of logic, so we’ll only look at those related to $mount:

class Watcher {
    constructor(
        vm: Component,
        expOrFn: string | Function,
        cb: Function,
        options?: ?Object, isRenderWatcher? : boolean){
        // ...
        if (typeof expOrFn === 'function') {
          this.getter = expOrFn
        }else {
          // ...
        }

        // ...
        this.value = this.lazy ? undefined : this.get()
    }

    get() {
      // ...
      value = this.getter.call(vm, vm)
      // ...
      // The cleanupDeps method will be analyzed later, which is important for performance optimization
      returnvalue; }}Copy the code

From the above code, we can see:

  • Call the beforeMount hook function first
  • Instantiate Watch with the updateComponent method as a parameter. Watch performs two functions: (1) it executes the callback function during initialization; (2) it executes the callback function when the monitored data in the VM instance changes. The second one examines the vm._update(vm._render(), hydrating) method after the callback is executed in the data change monitoring section, which has two steps: (1) Execute render method, return the latest VNode tree (2) call update method, actually do diff algorithm comparison, complete a render
  • Call the mounted hook function

3. To summarize

  1. Template, el () {render ();
  2. Call compileToFunctions to fetch the Render function and add it to options.render
  3. Calling mount.call actually calls the mountComponent function
  4. Call the beforeMount hook
  5. Instantiate render Watcher and perform the callback
  6. Get the VNode tree (actually a JS object) from the Render function
  7. Update, which is actually patch, and Vue performs the diff algorithm to complete a rendering
  8. Call mounted hook

Pay attention to reactive, compileToFunctions, virtual DOM, and patch code words in the following sections ~😽