Blogger Information
Blog 82
fans 0
comment 1
visits 107839
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
写于vue3.0发布前夕的helloworld之二
子龙的博客搬家啦httpswwwcnblogscomZiLongZiLong
Original
804 people have browsed it

接着,继续走,来到了vm.$mount。

开始生成render函数,生成VNode,由于是第一次加载,所以patch机制为只删除前一个dom节点机制,下面都会讲到。

先到$mount:

Vue.prototype.$mount = function (
    el,
    hydrating
  ) {
    el = el && query(el);

    /* istanbul ignore if */
    if (el === document.body || el === document.documentElement) {
      warn(
        "Do not mount Vue to <html> or <body> - mount to normal elements instead."
      );
      return this
    }

    var options = this.$options;
    // resolve template/el and convert to render function
    if (!options.render) {
      var template = options.template;
      if (template) {
        if (typeof template === 'string') {
          if (template.charAt(0) === '#') {
            template = idToTemplate(template);
            /* istanbul ignore if */
            if (!template) {
              warn(
                ("Template element not found or is empty: " + (options.template)),
                this
              );
            }
          }
        } else if (template.nodeType) {
          template = template.innerHTML;
        } else {
          {
            warn('invalid template option:' + template, this);
          }
          return this
        }
      } else if (el) {
        template = getOuterHTML(el);
      }
      if (template) {
        /* istanbul ignore if */
        if (config.performance && mark) {
          mark('compile');
        }

        var ref = compileToFunctions(template, {
          outputSourceRange: "development" !== 'production',
          shouldDecodeNewlines: shouldDecodeNewlines,
          shouldDecodeNewlinesForHref: shouldDecodeNewlinesForHref,
          delimiters: options.delimiters,
          comments: options.comments
        }, this);
        var render = ref.render;
        var staticRenderFns = ref.staticRenderFns;
        options.render = render;
        options.staticRenderFns = staticRenderFns;

        /* istanbul ignore if */
        if (config.performance && mark) {
          mark('compile end');
          measure(("vue " + (this._name) + " compile"), 'compile', 'compile end');
        }
      }
    }
    return mount.call(this, el, hydrating)
  };

一句句来讲,这里边是挺长,但是足够简单,首先判断el是否为body或者html元素,是则警告,然后退出当前函数,然后根据条件拿到template,因为我们没有设置render和template,所以template走else分支getOuterHTML,拿到template之后,会将当前的template编译成一个render函数,这个编译函数叫做compileToFunctions:这个函数是真的长,而且与主题干系不大,这里我们用文字解释一下就行,首先他是一个闭包函数,闭包了一个叫cache的对象这个对象是缓存当前已经编译过的模板的,进入函数之后有缓存直接返回缓存,没有的话就会执行compile函数,这些都是编译过程,最终compile会调用parseHTML方法使用正则加字符串分析字符串,返回一个这样的对象:

{   
    "type":1,
    "tag":"div",
    "attrsList":[
        {"name":"id","value":"app","start":5,"end":13}
    ],
    "attrsMap":{
        "id":"app"
    },
    "rawAttrsMap":{
        "id":{
            "name":"id",
            "value":"app",
            "start":5,
            "end":13
            }
    },
    "children":
    [
        {
            "type":2,
            "expression":"_s(msg)",
            "tokens":[{"@binding":"msg"}],
            "text":"{{msg}}",
            "start":14,"end":21
        }
    ],
    "start":0,
    "end":27,
    "plain":false,
    "attrs":[{"name":"id","value":"\"app\"","start":5,"end":13}]
}

这就是我们的ast语法树,之后会根据ast生成code,这个方法叫做generate:

function generate (
    ast,
    options
  ) {
    var state = new CodegenState(options);
    var code = ast ? genElement(ast, state) : '_c("div")';
    return {
      render: ("with(this){return " + code + "}"),
      staticRenderFns: state.staticRenderFns
    }
  }

这个方法调用了genElement方法,简而言之,里边的方法生成code时都会以ast上的值为原料,进行字符串拼接,最后code长这样:

_c('div',{attrs:{"id":"app"}},[_v(_s(msg))])

这里做一下解释_c,_v都是vue内部创建虚拟dom的函数,_s其实就是将当前值转化成字符串,继续走,generate执行完毕之后返回到compileToFunctions,这个时候拼接好的render字符串是这样:

"with(this){return _c('div',{attrs:{"id":"app"}},[_v(_s(msg))])}"

然后会使用new Function 将其生成一个函数,这个函数就是我们的render,组建上的render,执行完compileToFunctions,继续回到$mount函数里,执行最终的$mount:

Vue.prototype.$mount = function (
    el,
    hydrating
  ) {
    el = el && inBrowser ? query(el) : undefined;
    return mountComponent(this, el, hydrating)
  };

可见mout调用了mountComponent方法:

function mountComponent (
    vm,
    el,
    hydrating
  ) {
    vm.$el = el;
    if (!vm.$options.render) {
      vm.$options.render = createEmptyVNode;
      {
        /* 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');

    var updateComponent;
    /* istanbul ignore if */
    if (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 {
      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
  }

mountComponent方法一进来,先判断是否有render,之后会根据条件警告一些信息。然后开始调起beforeMount钩子函数,下来初始化一个updateComponent方法,他的正常版本长这样:

updateComponent = function () {
        vm._update(vm._render(), hydrating);
      };

实际上在组建的dom更新,包括第一次挂载,和随后的相应式更新上,本质都用的是这一个函数。然后继续走,重头戏来啦,watcher在这里开始初始化,注意传给他的第四个参数,在哪里我们把要调用beforeUpdate给传了过去。
我们的下面来到watcher的构造函数:

接下

Statement of this Website
The copyright of this blog article belongs to the blogger. Please specify the address when reprinting! If there is any infringement or violation of the law, please contact admin@php.cn Report processing!
All comments Speak rationally on civilized internet, please comply with News Comment Service Agreement
0 comments
Author's latest blog post