Skip to content

Vue2.5.16源码解读二:从入口文件进行阅读

接上回,我们确认了一个入口文件src/platforms/web/entry-runtime-with-compiler.js ,将该文件代码折叠如下图所示,方便一览全局: 可以看到这里import Vue from './runtime/index' 引入了一个Vue ,然后定义了下Vue.prototype.$mountVue.compile ,看Vue源码之前最好是先过一下Vue的官方文档,这里Vue.prototype.$mount 是一个关在Vue原型链上的属性,所以通过new Vue() 创建的Vue的实例都会继承这个$mount 方法,在看这个方法的作用之前,因为看过了官方文档,并且我之前用了两年的Vue了,所以先猜测下这个大概跟Vue组件生命周期里的mounted 钩子可能有关系,可能是这个$mount 方法执行完后就会触发组件生命周期里的mounted 这个钩子里写的程序。现在详细看下相关代码:

typescript
const mount = Vue.prototype.$mount;
// 返回类型是Component,这个类型下文具体讲,就直译为返回一个组件即可
Vue.prototype.$mount = function (
  // el是可选参数,值是字符串(css选择符)或dom元素(Element)
  el?: string | Element,
  // hydrating也是可选参数,布尔类型,以前用过redux-persist这个库,那里这种名词的意思是是否恢复之前的数据
  hydrating?: boolean,
): Component {
  // 如果el是元素则值维持不变,如果el是字符串则赋值为通过document.querySelector(el)匹配到的元素
  el = el && query(el);

  // 下面注释里的istanbul指的是一个叫istanbul的代码覆盖率工具
  /* istanbul ignore if */
  if (el === document.body || el === document.documentElement) {
    process.env.NODE_ENV !== "production" &&
      warn(
        // 如果元素是body/document.documentElement,且非针对生产环境打包的话,则提示用户不要将Vue绑定到html或body元素上
        `Do not mount Vue to <html> or <body> - mount to normal elements instead.`,
      );
    // 返回this
    return this;
  }

  // 一个$options属性
  const options = this.$options;
  // 如果没有定义render函数,则Vue会按下面这样去定义一个render函数
  // resolve template/el and convert to render function
  if (!options.render) {
    let template = options.template;
    if (template) {
      // 如果有模版
      if (typeof template === "string") {
        if (template.charAt(0) === "#") {
          // 如果template是一个id选择器(以#开头),则将template重新赋值为对应元素的innerHTML
          template = idToTemplate(template);
          /* istanbul ignore if */
          if (process.env.NODE_ENV !== "production" && !template) {
            // 如果id关联到的innerHTML为空或不存在,则在非生产版本下提示用户
            warn(
              `Template element not found or is empty: ${options.template}`,
              this,
            );
          }
        }
      } else if (template.nodeType) {
        // 如果template不是字符串,但是有nodeType属性,则判断template为dom,取其innerHTML作为模版
        template = template.innerHTML;
      } else {
        if (process.env.NODE_ENV !== "production") {
          warn("invalid template option:" + template, this);
        }
        // 返回this
        return this;
      }
    } else if (el) {
      // 如果没有模板但是有元素,就取元素的outerHtml或近似等价的东西
      template = getOuterHTML(el);
    }
    if (template) {
      /* istanbul ignore if */
      if (process.env.NODE_ENV !== "production" && config.performance && mark) {
        // 通过window.performance.mark来标记性能监测点'compile'
        mark("compile");
      }

      // 获得render和staticRenderFns函数,并挂到Vue.property.$options对象上
      const { render, staticRenderFns } = compileToFunctions(
        template,
        {
          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) {
        // 通过window.performance.mark标记性能监测点'compile end'
        mark("compile end");
        // 通过window.erformance.measure来测量compile和compile end这两个性能监测点间的用时
        measure(`vue ${this._name} compile`, "compile", "compile end");
      }
    }
  }
  // 再次调用mount方法
  return mount.call(this, el, hydrating);
};

好了,这篇文章对入口文件的分析就写到这里,后续我们将以入口文件为线索展开阅读其他部分的代码。打算看一下Vue.compile = compileToFunctions 这里的compileToFunctions 方法,以及import Vue from './runtime/index' 这里导入的Vue

天上的神明与星辰,人间的艺术和真纯,我们所敬畏和热爱的,莫过于此。