vue源码解读系列(5):分析dom挂载阶段,了解$mount的实现

入口文件:
这里先缓存了起来,const mount = Vue.prototype.$mount,这里的 Vue.prototype.$mount 是在 ./runtime/index 中定义的。这里是复用了 runtime only 版本初始化生命周期的一些代码,因为runtime only 版本是没有挂载 dom 的,所以要在这里重新写挂载dom的代码,如下:

// src/platforms/web/entry-runtime-with-compiler.js

import Vue from './runtime/index'

const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
     
  // query(el) 会判断是字符串就报警告并返回一个空的div,是dom就直接返回一个dom,所以我们一般就是 
el = el && query(el) /* istanbul ignore if */ // 这里判断你传入的 dom 不能是body或者 html,因为你会把整个body覆盖 if (el === document.body || el === document.documentElement) { process.env.NODE_ENV !== 'production' && warn( `Do not mount Vue to or - mount to normal elements instead.` ) return this } const options = this.$options // resolve template/el and convert to render function // 这里判断不是render函数(写template模板)的处理 if (!options.render) { // 拿到template let template = options.template if (template) { // 如果template是字符串 if (typeof template === 'string') { if (template.charAt(0) === '#') { template = idToTemplate(template) /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && !template) { warn( `Template element not found or is empty: ${ options.template}`, this ) } } // 如果template是标签 } else if (template.nodeType) { template = template.innerHTML } else { // 如果template是其他,报警告 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') } } } // 如果有render函数,就执行这个 // mount就是之前缓存的Vue.prototype.$mount,也就是runtime/index.js里面定义的,里面返回了mountComponent函数,我们来看看mountComponent里面是什么,下一个 return mount.call(this, el, hydrating) } /** * Get outerHTML of elements, taking care * of SVG elements in IE as well. */ function getOuterHTML (el: Element): string { if (el.outerHTML) { return el.outerHTML } else { const container = document.createElement('div') container.appendChild(el.cloneNode(true)) return container.innerHTML } } Vue.compile = compileToFunctions
// src/core/instance/lifecycle.js

export function mountComponent (
  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) !== '#') ||
        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
}
import Vue form 'vue'

var app = new Vue({
     
	// render和 el写一个就好
	render: h => h(App),
	el: '#app',
	data () {
     
		message: 'hello word'
	},
	render (h) {
     
	    return h('div', {
     
	      class: 'menu-nav'
	    })
  	}
})

你可能感兴趣的:(vue)