SoFunction
Updated on 2025-03-09

Explain details what happens when vue is mounted on dom

What happens after vue is mounted to the dom element

The previous article analyzed the operations performed when new vue() is initialized, mainly including calling vue._init to perform a series of initializations, including life cycle, event system, beforeCreate and Created hooks. This happens here, focusing on the initialization of initState, that is, the data props computed we commonly use, etc. Finally, $mount is executed to mount the dom. This article will further elaborate on what happened after mount.

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

The code of mount is very simple, and the moutComponent method is executed directly.

export function mountComponent (
 vm: Component,
 el: ?Element,
 hydrating?: boolean
): Component {
 vm.$el = el
 if (!vm.$) {
 vm.$ = createEmptyVNode
 if (.NODE_ENV !== 'production') {
  /* istanbul ignore if */
  if ((vm.$ && vm.$(0) !== '#') ||
  vm.$ || 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 (.NODE_ENV !== 'production' &&  && 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 (. 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
}

moutComponent Here we judge the render function. During normal development, there are many ways to write dom. You can write temple, render function, or write dom directly in the mounting element. However, in the compilation stage (usually executed through webpack), all of these writing methods will be compiled into render function. Therefore, the final execution of the render function. After judging the render, you can see that beforeMount hook is executed here, and finally execute new Watcher(). We enter new Watcher

export default class Watcher {
 vm: Component;
 expression: string;
 cb: Function;
 id: number;
 deep: boolean;
 user: boolean;
 lazy: boolean;
 sync: boolean;
 dirty: boolean;
 active: boolean;
 deps: Array<Dep>;
 newDeps: Array<Dep>;
 depIds: SimpleSet;
 newDepIds: SimpleSet;
 before: ?Function;
 getter: Function;
 value: any;

 constructor (
 vm: Component,
 expOrFn: string | Function,
 cb: Function,
 options?: ?Object,
 isRenderWatcher?: boolean
 ) {
  = vm
 if (isRenderWatcher) {
  vm._watcher = this
 }
 vm._watchers.push(this)
 // options
 if (options) {
   = !!
   = !!
   = !!
   = !!
   = 
 } else {
   =  =  =  = false
 }
  = cb
  = ++uid // uid for batching
  = true
  =  // for lazy watchers
  = []
  = []
  = new Set()
  = new Set()
  = .NODE_ENV !== 'production'
  ? ()
  : ''
 // parse expression for getter
 if (typeof expOrFn === 'function') {
   = expOrFn
 } else {
   = parsePath(expOrFn)
  if (!) {
   = noop
  .NODE_ENV !== 'production' && warn(
   `Failed watching path: "${expOrFn}" ` +
   'Watcher only accepts simple dot-delimited paths. ' +
   'For full control, use a function instead.',
   vm
  )
  }
 }
  = 
  ? undefined
  : ()
 }

I won't mention other methods for now, you can see, but we can also roughly guess what he is doing. Here we just intercept some of the watcher's constructor methods. The key is to execute it in the end, and then execute it. Finally, it is equivalent to executing the second parameter passed in the Watcher constructor method, which is the updateComponent method in the previous link. The updateComponent method is also defined in the moutComponent method.

updateComponent = () => {
 vm._update(vm._render(), hydrating)
 }

Here we first execute the compiled render method, and then pass it to the _update method as a parameter to execute. After the render method is executed, it returns a vnode, namely Virtual dom, and then pass this Virtual dom as a parameter to the update method. Here we first introduce the Virtual dom and then introduce the update method that performs the mount at the end.

render function

._render = function (): VNode {
 const vm: Component = this
 const { render, _parentVnode } = vm.$options

 if (_parentVnode) {
  vm.$scopedSlots = normalizeScopedSlots(
  _parentVnode.,
  vm.$slots
  )
 }

 // set parent vnode. this allows render functions to have access
 // to the data on the placeholder node.
 vm.$vnode = _parentVnode
 // render self
 let vnode
 try {
  vnode = (vm._renderProxy, vm.$createElement)
 } catch (e) {
  handleError(e, vm, `render`)
  // return error render result,
  // or previous vnode to prevent render error causing blank component
  /* istanbul ignore else */
  if (.NODE_ENV !== 'production' && vm.$) {
  try {
   vnode = vm.$(vm._renderProxy, vm.$createElement, e)
  } catch (e) {
   handleError(e, vm, `renderError`)
   vnode = vm._vnode
  }
  } else {
  vnode = vm._vnode
  }
 }
 // if the returned array contains only a single node, allow it
 if ((vnode) &&  === 1) {
  vnode = vnode[0]
 }
 // return empty vnode in case the render function errored out
 if (!(vnode instanceof VNode)) {
  if (.NODE_ENV !== 'production' && (vnode)) {
  warn(
   'Multiple root nodes returned from render function. Render function ' +
   'should return a single root node.',
   vm
  )
  }
  vnode = createEmptyVNode()
 }
 // set parent
  = _parentVnode
 return vnode
 }

According to the type definition of flow, we can see that the _render function finally returns a vnode, and the main code of _render In the first try catch, vnode = (vm._renderProxy,vm.$CREATRElement) , the first parameter is the current context this is actually the vm itself, and the second parameter is the actual execution method. When we are handwriting the render function, for example, this

 render:h=>{
  return h(
    "div",
    123
   )
  }

At this time, we use h, which is the $createElement method passed in, and then let's take a look at the createElement method. Before looking at the createElement, let's briefly introduce vdom, because createElement returns a vdom, and vdom is actually a mapping of the real dom object, mainly including the tag name tag and the tag children below it, as well as some attribute definitions, etc. When we are changing the dom, the first thing is the data change, and the data change maps to In vdom, then change vdom. Vdom is a change to the js data level, so the cost is very small. In this process, we can also perform targeted optimization, reuse, etc., and finally operate the optimized change part to the real dom through dom operations. In addition, through the definition of vdom, we can not only map vdom to web document streams, but even to document streams on the app side. There are many document streams for desktop applications. Here we quote the vue js author's evaluation of vdom: The real value of Virtual DOM has never been performance, but it 1: Opens the door for functional UI programming methods, 2: It can be rendered to backends other than dom, such as ReactNative.

Let's continue to introduce creatElement

export function _createElement (
 context: Component,
 tag?: string | Class<Component> | Function | Object,
 data?: VNodeData,
 children?: any,
 normalizationType?: number
): VNode | Array<VNode> {
 if (isDef(data) && isDef((data: any).__ob__)) {
 .NODE_ENV !== 'production' && warn(
  `Avoid using observed data object as vnode data: ${(data)}\n` +
  'Always create fresh vnode data objects in each render!',
  context
 )
 return createEmptyVNode()
 }
 // object syntax in v-bind
 if (isDef(data) && isDef()) {
 tag = 
 }
 if (!tag) {
 // in case of component :is set to falsy value
 return createEmptyVNode()
 }
 // warn against non-primitive key
 if (.NODE_ENV !== 'production' &&
 isDef(data) && isDef() && !isPrimitive()
 ) {
 if (!__WEEX__ || !('@binding' in )) {
  warn(
  'Avoid using non-primitive value as key, ' +
  'use string/number value instead.',
  context
  )
 }
 }
 // support single function children as default scoped slot
 if ((children) &&
 typeof children[0] === 'function'
 ) {
 data = data || {}
  = { default: children[0] }
  = 0
 }
 if (normalizationType === ALWAYS_NORMALIZE) {
 children = normalizeChildren(children)
 } else if (normalizationType === SIMPLE_NORMALIZE) {
 children = simpleNormalizeChildren(children)
 }
 let vnode, ns
 if (typeof tag === 'string') {
 let Ctor
 ns = (context.$vnode && context.$) || (tag)
 if ((tag)) {
  // platform built-in elements
  vnode = new VNode(
  (tag), data, children,
  undefined, undefined, context
  )
 } else if ((!data || !) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
  // component
  vnode = createComponent(Ctor, data, context, children, tag)
 } else {
  // unknown or unlisted namespaced elements
  // check at runtime because it may get assigned a namespace when its
  // parent normalizes children
  vnode = new VNode(
  tag, data, children,
  undefined, undefined, context
  )
 }
 } else {
 // direct component options / constructor
 vnode = createComponent(tag, data, context, children)
 }
 if ((vnode)) {
 return vnode
 } else if (isDef(vnode)) {
 if (isDef(ns)) applyNS(vnode, ns)
 if (isDef(data)) registerDeepBindings(data)
 return vnode
 } else {
 return createEmptyVNode()
 }
}

CreatElement returns a new VNode in the final result, and the parameters passed in craete are processed and passed to the initialization of VNode. There are several situations here. CreateEmptyVNode, no parameters are passed, or the parameters are incorrect, and an empty vnode will be returned. If the browser tag is tagged such as div h3 p, it will return a reserved VNode, etc. Finally, return to the above, after the vnode is created, _render will return this vnode, and finally go back to vm._update(). Update, it is to insert vnode into the real document stream through the dom operation. In the next section, we will talk about update

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.