Basic process of instance mounting
After the options are merged, Vue makes a series of function calls, including initializing the event center, defining rendering functions, and so on. In_ At the end of the init method, the object el is
Option. If the el option exists, the $mount method will be called to mount the instance.
/* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { initProxy(vm) } else { vm._renderProxy = vm } // expose real self vm._self = vm // Declaration cycle initialization initLifecycle(vm) // Event center initialization initEvents(vm) // Initialize rendering initRender(vm) callHook(vm, 'beforeCreate') initInjections(vm) // resolve injections before data/props initState(vm) initProvide(vm) // resolve provide after data/props callHook(vm, 'created') // If the el option exists, the calling method is hung on the element if (vm.$options.el) { vm.$mount(vm.$options.el) }
When looking for the definition of the $mount method, we will find that this method is defined in multiple locations of the Vue source code. This is because the implementation of the $mount method is related to the platform and construction method. In this series of articles, we focus on the implementation of the $mount method in the runtime with compiler version. Let's first look at the implementation of the $mount method
// First, cache the $mount method on the Vue constructor prototype, which is a common mount method const mount = Vue.prototype.$mount // Redefine the $mount method for the Runtim with compiler version Vue.prototype.$mount = function ( el?: string | Element, hydrating?: boolean ): Component { el = el && query(el) /* istanbul ignore if */ 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 /** * If the render method is not defined, convert el or template to render method */ if (!options.render) { let template = options.template if (template) { if (typeof template === 'string') { if (template.charAt(0) === '#') { // Find the template element and return the template 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) { // The passed in template is a dom node template = template.innerHTML } else { if (process.env.NODE_ENV !== 'production') { warn('invalid template option:' + template, this) } return this } } else if (el) { // If the template option is not passed in, the element node corresponding to the el option is used as the template template = getOuterHTML(el) } if (template) { /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { mark('compile') } // Convert template to render method const { render, staticRenderFns } = compileToFunctions(template, { outputSourceRange: process.env.NODE_ENV !== 'production', shouldDecodeNewlines, shouldDecodeNewlinesForHref, delimiters: options.delimiters, comments: options.comments }, this) // Save the converted render method to options for subsequent use 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') } } } // Call the mount method to mount the element return mount.call(this, el, hydrating) }
In this code, the $mount method on the Vue prototype (which is a common mount method for multiple platforms and environments) is cached first, and then the method is redefined. Next, determine if there is a definition of the render method. If there is no definition of the render method, we need to get the template through the el or template option, then compile the template to get the render method, and finally call the caching method to mount the element.
Now let's look at the implementation of the cached $mount method
// platforms/web/runtime/index.js Vue.prototype.$mount = function ( el?: string | Element, hydrating?: boolean ): Component { el = el && inBrowser ? query(el) : undefined return mountComponent(this, el, hydrating) }
It can be seen that a mountComponent method is called inside the cached $mount (the implementation of this method will be analyzed later, and the basic functions will be briefly introduced here). The core of this method is to instantiate a rendering watcher, and the updateComponent method will be called in Wathcer to render elements. The rendering Watcher will be executed in two places: one is during initialization, that is, when the instance is mounted; On the other hand, when the data in the Vue instance changes, execute again to update the page.
export function mountComponent ( vm: Component, el: ?Element, hydrating?: boolean ): Component { vm.$el = el if (!vm.$options.render) { vm.$options.render = createEmptyVNode } 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 = () => { // Call_ update method to convert virtual DOM into real DOM vm._update(vm._render(), hydrating) } } /** * Create a render watcher and execute the updateComponent callback function when the data changes * When the instance is first mounted, the updateComponent method will also be executed once */ 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 }
summary
Let's summarize the mounting process of Vue instances
- Determine the mounted DOM element, which cannot be used as the root nodes such as html and body
- Determine the rendering method. If the render method is defined in the passed in options, it can be used directly; If you define the render method for, you need to compile the template to generate the render method
- Regardless of the source of the render method, the mountComponent will be executed at last, and a render watcher will be created at this stage
- In the function updateComponent returned in the render watcher, there are two stages: one is to call VM_ Render method, one is to call VM_ Update method. Next, we will analyze the implementation and functions of these two methods