Vue instance mount implementation

When executing new Vue, Vue determines if the EL parameter is defined and executes $mount to mount the VM

$mount

/ / in the SRC/core/instance/init. Jsif (vm.$options.el) {
        vm.$mount(vm.$options.el)
    }
Copy the code

Override the $mount method on the prototype

Pure front-end browser environment Compiler version of $mount

  • An error is reported if el points to the body or HTML root node
  • Check if the current instance defines a Render function. If not, fetch Template and fetch render via compileToFunctions
  • Finally, call the mount method on the prototype
// src/platform/web/entry-runtime-with-compiler.js

    const mount = Vue.prototype.$mount
    Vue.prototype.$mount = function( el? : string | Element, hydrating? : boolean ): Component { el = el && query(el) /* istanbul ignoreif* /if(el === document.body || el === document.documentElement) { process.env.NODE_ENV ! = ='production' && warn(
            `Do not mount Vue to <html> or <body> - mount to normal elements instead.`
            )
            return this
        }

        const options = this.$options
        // resolve template/el and convert to render function
        if(! options.render) {let template = options.template
            if (template) {
                if (typeof template === 'string') {
                    if (template.charAt(0) === The '#') {
                    template = idToTemplate(template)
                    /* istanbul ignore if* /if(process.env.NODE_ENV ! = ='production' && !template) {
                        warn(
                        `Template element not found or is empty: ${options.template}`,
                        this
                        )
                    }
                }
            } else if (template.nodeType) {
                template = template.innerHTML
            } else {
                if(process.env.NODE_ENV ! = ='production') {
                warn('invalid template option:' + template, this)
                }
                return this
            }
            } else if (el) {
                template = getOuterHTML(el)
            }
            if (template) {
                /* istanbul ignore if* /if(process.env.NODE_ENV ! = ='production' && config.performance && mark) {
                    mark('compile') } const { render, staticRenderFns } = compileToFunctions(template, { outputSourceRange: process.env.NODE_ENV ! = ='production',
                    shouldDecodeNewlines,
                    shouldDecodeNewlinesForHref,
                    delimiters: options.delimiters,
                    comments: options.comments
                }, this)
                options.render = render
                options.staticRenderFns = staticRenderFns

                /* istanbul ignore if* /if(process.env.NODE_ENV ! = ='production' && config.performance && mark) {
                    mark('compile end')
                    measure(`vue ${this._name} compile`, 'compile'.'compile end')}}}return mount.call(this, el, hydrating)
    }
Copy the code

Implementation of the prototype $mount

$mount on the prototype method defined in SRC/platforms/web/runtime/index, js, actually is to call the mountComponent method, this method defined in SRC/core/instance/lifecycle. Js

The first parameter is el, which represents the mounted element. It can be a string or a DOM object. If it is a string, the browser will call the Query method to convert it into a DOM object. The second parameter is related to server rendering. In the browser environment we do not need to pass the second parameter

// public mount method
    Vue.prototype.$mount = function( el? : string | Element, hydrating? : boolean ): Component { el = el &&inBrowser ? query(el) : undefined
        return mountComponent(this, el, hydrating)
    }
Copy the code

MountComponent method

  1. Create an empty VNode if there is no render method on options
  2. Execute beforeMount hooks
  3. Defining the updateComponent method actually calls vm._update to trigger the update
  4. Instantiate a render Watcher that listens for updates. Add beforeUpdate hooks to the beforeUpdate hook function. Before Update hooks are executed after component generation and before destruction
  5. Vm._update () executes mounted hook after patch VNode to the DOM. If vm.$vnode is null, this is not a component initialization, but an external new Vue initialization
export functionmountComponent( vm: Component, el: ? Element, hydrating? : boolean ): Component { vm.$el = el
    if(! vm.$options.render) {
        vm.$options.render = createEmptyVNode
        if(process.env.NODE_ENV ! = ='production') {
            /* istanbul ignore if* /if ((vm.$options.template && vm.$options.template.charAt(0) ! = =The '#') ||
                vm.$options.el || el) {
                warn(
                    'You are using the runtime-only build of Vue where the template ' +
                    'compiler is not available. Either pre-compile the templates into ' +
                    'render functions, or use the compiler-included build.',
                    vm
                )
            } else {
                warn(
                    'Failed to mount component: template or render function not defined.',
                    vm
                )
            }
        }
    }
    callHook(vm, 'beforeMount')

    let updateComponent
    /* istanbul ignore if* /if(process.env.NODE_ENV ! = ='production' && config.performance && mark) {
        updateComponent = () => {
            const name = vm._name
            const id = vm._uid
            const startTag = `vue-perf-start:${id}`
            const endTag = `vue-perf-end:${id}`

            mark(startTag)
            const vnode = vm._render()
            mark(endTag)
            measure(`vue ${name} render`, startTag, endTag)

            mark(startTag)
            vm._update(vnode, hydrating)
            mark(endTag)
            measure(`vue ${name} patch`, startTag, endTag)
        }
    } else {
        updateComponent = () => {
            vm._update(vm._render(), hydrating)
        }
    }

    // we set this to vm._watcher inside the watcher's constructor // since the watcher's initial patch may call $forceUpdate (e.g. inside child
    // component's mounted hook), which relies on vm._watcher being already defined new Watcher(vm, updateComponent, noop, { before() { if (vm._isMounted && ! vm._isDestroyed) { callHook(vm, 'beforeUpdate') } } }, true /* isRenderWatcher */) hydrating = false // manually mounted instance, call mounted on self // mounted is called for render-created child components in its inserted hook if (vm.$vnode == null) { vm._isMounted = true callHook(vm, 'mounted') } return vm }Copy the code

If vm.$vnode is null, it indicates that this is not a component initialization, but that we initialized it through an external new Vue

// manually mounted instance, call mounted on self
    // mounted is called for render-created child components in its inserted hook
    if (vm.$vnode == null) {
        vm._isMounted = true
        callHook(vm, 'mounted')}Copy the code

Hey, big guy. Star. Thank you