Explanation of the difference between vue $mount and el

  • 2021-08-16 23:01:16
  • OfStack

There is no difference in the use of the two, both to mount the instantiated vue into the specified dom element.

If el is specified when instantiating vue, the vue will be rendered in dom corresponding to el. Otherwise, if el is not specified, the vue instance will be in an "unmounted" state, and the mount can be performed manually through $mount.

Note: If $mount does not provide parameters, the template will be rendered as an element outside the document, and you must insert it into the document using the native DOM API.

For example:


var MyComponent = Vue.extend({
 template: '<div>Hello!</div>'
})

//  Create and mount to the  #app ( Will replace  #app)
new MyComponent().$mount('#app')

//  Ibid. 
new MyComponent({ el: '#app' })

//  Or, render outside the document and then mount it 
var component = new MyComponent().$mount()
document.getElementById('app').appendChild(component.$el)

Additional knowledge: Implementation of the Vue instance mount method ($mount)

The beforeCreate and created lifecycle hooks have been called back in the _init method of Vue, after which the instance is mounted


  if (vm.$options.el) { //  Mount an instance 
   vm.$mount(vm.$options.el);
  }

In the mount function, the callback of beforeMount and mounted will be made.

The implementation of the $mount function is different under different platforms. Consider the runtime-with-compiler version of web platform below, and its definition under web platform is as follows (src/platforms/web/runtime/index. js)


import { mountComponent } from 'core/instance/lifecycle';

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

In the parameters of the $mount function, the first is el of our attribute, and the second parameter is related to server rendering, which is used in patch function and can be ignored here.

But when you call this $mount function, you first call the $mount function under different versions, and then call the $mount function of the corresponding platform in this function, as follows. In runtime-with-compiler version, the $mount function is as follows (src/platforms/web/entry-runtime-with-compiler. js)


import Vue from './runtime/index';
const mount = Vue.prototype.$mount; //  Cache   Above  $mount  Method 
Vue.prototype.$mount = function(
 el?: string | Element,
 hydrating?: boolean
): Component {
 el = el && query(el);

 //  Unable to mount to  body  And  html  Upper 
 if (el === document.body || el === document.documentElement) {   
  return this;
 }

 const options = this.$options;

 if (!options.render) { //  If not  render  Function 
  // ...  Will  render  Function is added to the  options  Upper 
   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;
  // ...
 }
 
 return mount.call(this, el, hydrating);
};

It can be seen that this function mainly does three things

1. It is restricted from mounting on body and html because the mounted object will be replaced after mounting

2. If there is no render () function in the current Vue instance (write template, etc.), the render function is added to options by compiling and other means

3. Call the $mount method that we cached at the beginning of the code, which is the method under web platform.

Under the web platform, the $mount method mainly calls the mountComponent () method, and then our core is this method

We found the definition of this method in the file 'core/instance/lifecycle. js. After deleting 1 non-important code, we read as follows


export function mountComponent(
 vm: Component,
 el: ?Element,
 hydrating?: boolean
): Component {
 vm.$el = el;
 if (!vm.$options.render) { 
  //  Not the point, the department is mainly used for not  render  Under the function 1 Some error hints 
 }
 callHook(vm, 'beforeMount'); //  Callback  beforeMount ,  Start preparing to mount the instance 

 //  Declaration   Update component   Function of   (The source code is related to performance Configuration is not the focus, so omit)  
 const updateComponent = updateComponent = () => {
   vm._update(vm._render(), hydrating);
 };

 // new 1 A  Watcher [isRenderWatcher]
 new Watcher(vm, updateComponent, noop, {
  before() {
   if (vm._isMounted && !vm._isDestroyed) {
    callHook(vm, 'beforeUpdate');
   }
  },
 }, true /* isRenderWatcher */);
 hydrating = false;

 // Vue  Of the root instance of  mounted  The callback is executed here 
 if (vm.$vnode == null) {
  vm._isMounted = true;
  callHook(vm, 'mounted');
 }
 
 return vm;
}

The above code mainly does the following three things

1. Callback beforeMount

2. Generate updateComponent method, which render vnode to real DOM

3. new 1 Watcher, and the updateComponent method is called in this Watcher

4. Callback mounted

The updateComponent method is more complicated, and its internal call is mainly _ update () to render vnode as the real DOM displayed on the browser

Let's consider the following two questions

1. How to call updateComponent method in Watcher

The constructor of the Watcher function takes the following arguments


constructor(
  vm: Component,
  expOrFn: string | Function,
  cb: Function,
  options?: ?Object,
  isRenderWatcher?: boolean
 )

In the above code, the updateComponent () method is passed as the second argument, expOrFn in the constructor

Look down and you'll see


  if (typeof expOrFn === 'function') {
   this.getter = expOrFn;
  }

That is, the updateComponent () method is set to the getter () method

See the last of the constructor


  this.value = this.lazy
   ? undefined
   : this.get();

Where the value of the lazy attribute is previously set to false

this. lazy =! ! options. lazy; //We don't have the lazy attribute in options

That is, this. get () is called at the end of the i constructor, whereas in this. get ()


  const vm = this.vm;
  try {
   value = this.getter.call(vm, vm);
  }

We see that the getter () method is called, that is, the updateComponent () method is called.

2. Why is $vnode empty for the root instance

There is the following code in the initRender () function

const parentVnode = vm.$vnode = options._parentVnode;

That is, the current actual value of $vnode is the vnode value of its parent node

The root instance has no parent node, so its $vnode value is empty, so it will be executed


 if (vm.$vnode == null) {
  vm._isMounted = true;
  callHook(vm, 'mounted');
 }

So where is the mounted callback of the child node performed?

There is the following code in the path () (core/vdom/patch. js) function


  if (vm.$options.el) { //  Mount an instance 
   vm.$mount(vm.$options.el);
  }
0

While looping through queue, the insert () method is called, which is VNodeHooks and is declared in componentVNodeHooks (core/vdom/create-component. js) with the following code


  if (vm.$options.el) { //  Mount an instance 
   vm.$mount(vm.$options.el);
  }
1

Since the path () method is called in the _ update () function, this article will not focus on it.

In the next section, we will talk about the implementation of the render () and _ update () methods


Related articles: