vue源码分析

Vue $mount函数

2020-10-29  本文已影响0人  Wendy81
// public mount method
Vue.prototype.$mount = function (
  el,
  hydrating
) {
  el = el && inBrowser ? query(el) : undefined;
  return mountComponent(this, el, hydrating)
};

这里看到$mount的函数在Vue的原型链上,Vue的任何实例都可以直接调用这个方法

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false
//Vue的实例调用$mount
new Vue({
  render: h => h(App),
}).$mount('#app')

接下来我们来查看返回的函数mountComponent运行情况

function mountComponent (
  vm,
  el,
  hydrating
) {
  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
        );
      }
    }
  }
  // step 1 - vm.$options.render = true
  callHook(vm, 'beforeMount');

  var updateComponent;
  /* istanbul ignore if */
  if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
    updateComponent = function () {
      var name = vm._name;
      var id = vm._uid;
      var startTag = "vue-perf-start:" + id;
      var endTag = "vue-perf-end:" + id;

      mark(startTag);
      var 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 {

 // step 2 - config.performance = false
    updateComponent = function () {
      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: function 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
}

$mount的使用

一、vm..$mount('#app')

new Vue({
  render: h => h(App),
}).$mount('#app')

来源:
step1:this._init(options)

Vue.prototype._init = function (options) {
......
initRender(vm);
......
}

step2:initRender(vm);

function initRender (vm) {
......
  // bind the createElement fn to this instance
  // so that we get proper render context inside it.
  // args order: tag, data, children, normalizationType, alwaysNormalize
  // internal version is used by render functions compiled from templates
  vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); };
  // normalization is always applied for the public version, used in
  // user-written render functions.
  vm.$createElement = function (a, b, c, d) { return createElement(vm, a, b, c, d, true); };
......
}

step3:initRender(vm);

function createComponent (
  Ctor,
  data,
  context,
  children,
  tag
) {
......
  // install component management hooks onto the placeholder node
  installComponentHooks(data);
......
}

step3:installComponentHooks

function installComponentHooks (data) {
  var hooks = data.hook || (data.hook = {});
  for (var i = 0; i < hooksToMerge.length; i++) {
    var key = hooksToMerge[i];
    var existing = hooks[key];
    var toMerge = componentVNodeHooks[key];
    if (existing !== toMerge && !(existing && existing._merged)) {
      hooks[key] = existing ? mergeHook$1(toMerge, existing) : toMerge;
    }
  }
}

step4:hooksToMerge

var hooksToMerge = Object.keys(componentVNodeHooks);

step5:componentVNodeHooks

// inline hooks to be invoked on component VNodes during patch
var componentVNodeHooks = {
  init: function init (vnode, hydrating) {
    if (
      vnode.componentInstance &&
      !vnode.componentInstance._isDestroyed &&
      vnode.data.keepAlive
    ) {
      // kept-alive components, treat as a patch
      var mountedNode = vnode; // work around flow
      componentVNodeHooks.prepatch(mountedNode, mountedNode);
    } else {

     //这里我们可以找到child,即是我们各个Vue实例,生成一个实例,执行一次$mount函数
      var child = vnode.componentInstance = createComponentInstanceForVnode(
        vnode,
        activeInstance
      );

      //当生成Vue实例后可以$mount生成真正的dom
      child.$mount(hydrating ? vnode.elm : undefined, hydrating);
    }
  },
......

二、增加Vue的option {el:'#app'}

new Vue({
  el:'#app',
  render: h => h(App),
})

实现方式是:我们可以看到vm.options.el如果存在,则实例初始化时就直接运行了mount函数

Vue.prototype._init = function (options) {
......
    if (vm.$options.el) {
      vm.$mount(vm.$options.el);
    }
}
上一篇 下一篇

猜你喜欢

热点阅读