vue $mount 的实现 vue 中是通过$mount 实例方法挂载 vm, 本质是把 template 和 el 转成 render 方法 转成 render 方法后,执行 mountComponent 本质是一个 new Watcher 的实现,当 vm 实例中监测到的数据发生变化的时候执行 updateComponent,进而渲染出 vm._render(vnode)

vue $mount 的实现

vue 中是通过$mount 实例方法挂载 vm,

  1. compiler 版本的 \$monut 实现

    本质是把 template 和 el 转成 render 方法

// src/platform/web/entry-runtime-with-compiler.js
// 先把 $mount缓存起来
const mount = Vue.prototype.$mount;
// 然后重新定义
Vue.prototype.$mount = function(el?: string | Element, hydrating?: boolean): Component {
  // el既可以是字符穿也可以是dom对象
  el = el && query(el);

  // 对el进行限制,不能挂载在html和body这样的根节点上
  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;
  }

  const options = this.$options;
  // 如果没有定义render方法,会把el或template转换成render方法
  if (!options.render) {
    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);
          }
        }
      } 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) {
      // template不存在,获取到outHTML
      template = getOuterHTML(el);
    }
    // 编译相关
    if (template) {
      /* istanbul ignore if */
      if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
        mark('compile');
      }
      // compileToFunctions是一个在线编译的过程
      const { render, staticRenderFns } = compileToFunctions(
        template,
        {
          shouldDecodeNewlines,
          shouldDecodeNewlinesForHref,
          delimiters: options.delimiters,
          comments: options.comments
        },
        this
      );
      // render函数
      options.render = 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');
      }
    }
  }
  // 调用原先$mount方法挂载
  return mount.call(this, el, hydrating);
};
  1. runtime $mount实现方式

    转成 render 方法后,执行 mountComponent

// src\platforms\web\runtime\index.js
// $mount支持两个参数,第一个是el,第二个是服务端渲染相关
Vue.prototype.$mount = function(el?: string | Element, hydrating?: boolean): Component {
  el = el && inBrowser ? query(el) : undefined;
  return mountComponent(this, el, hydrating);
};
  1. mountComponent的实现

    本质是一个 new Watcher 的实现,当 vm 实例中监测到的数据发生变化的时候执行 updateComponent,进而渲染出 vm._render(vnode)

// src/core/instance/lifecycle.js
export function mountComponent(vm: Component, el: ?Element, hydrating?: boolean): Component {
  vm.$el = el;
  // 如果render不存在,或者template编译不成功
  if (!vm.$options.render) {
    vm.$options.render = createEmptyVNode;
    if (process.env.NODE_ENV !== 'production') {
      // 报一个警告:如果用了runtime only编译方法,又写了template了,就会报了警告
      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 {
        // 报一个警告:如果用了runtime only编译方法,没有template或者render函数
        warn('Failed to mount component: template or render function not defined.', vm);
      }
    }
  }
  callHook(vm, 'beforeMount');
  let updateComponent;
  // 性能埋点
  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 = () => {
      // 渲染出vnode
      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
  // 初始化的时候执行回调函数
  // 当vm实例中监测到的数据发生变化的时候执行updateComponent
  new Watcher(
    vm,
    updateComponent,
    noop,
    {
      before() {
        if (vm._isMounted) {
          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;
}

暂无评论