vue源码分析-5-模版编译入口文件

266 阅读1分钟

runtime/index.js执行完以后,执行了entry-runtime-with-compiler.js,此入口文件主要定了Vue.prototype.$mount()方法,我们知道runtime/index.js也定义了Vue.prototype.$mount()方法,那么是否冲突了呢,并不是,因为此文件中定义的$mount方法会调用runtime/index.js中定义的$mount方法

两个方法的区别就是,entry-runtime-with-compiler.js中定义的$mount方法主要是为了进行模板编译,即将options.template中定义的模板编译为render函数

runtime/index.js中的方法主要是进行渲染界面,即将vnode渲染成真实的dom

// 这个其实是runtime中的$mount方法
const mount = Vue.prototype.$mount
// 覆盖了runtime中的$mount方法
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  // 获得element
  el = el && query(el)

  /* istanbul ignore if */
  // 如果el是body,或者是document,报警告
  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
  }

  // 拿到options参数
  const options = this.$options
  // resolve template/el and convert to render function
  // 如果不是render函数
  if (!options.render) {
    // 拿到options.template属性的值
    let template = options.template
    // 如果设置了template属性
    if (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 {
        // 否则报警告
        if (process.env.NODE_ENV !== 'production') {
          warn('invalid template option:' + template, this)
        }
        return this
      }
    } else if (el) {
      // 否则去拿到el的子节点
      template = getOuterHTML(el)
    }
    if (template) {
      // 性能埋点
      /* istanbul ignore if */
      if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
        mark('compile')
      }

      // 将模板编译为render函数
      const { render, staticRenderFns } = compileToFunctions(template, {
        outputSourceRange: process.env.NODE_ENV !== 'production',
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        // 改变纯文本插入分隔符 默认是  ["{{", "}}"]  如果改成 ['${', '}']  那么模板上就可以用 ${}去包裹数据了
        delimiters: options.delimiters,
        // 当设为 true 时,将会保留且渲染模板中的 HTML 注释。默认行为是舍弃它们。
        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')
      }
    }
  }
  // 执行runtime中的$mount方法
  return mount.call(this, el, hydrating)
}

关键的compileToFunctions()方法将在模板编译章节讲解

至此Vue的构造方法和一些基本方法都定义完成,接下来就需要实例化new Vue(options)使用Vue了,请见new Vue(options),让我们看看new Vue(options)都调用了什么方法