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

OK。接上回到render:

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

接着开始执行_s:一言以蔽之,就是undefined 和null输出一个空字符串,其他类型需要转换,然后开始执行_v方法:

function createTextVNode (val) {
    return new VNode(undefined, undefined, undefined, String(val))
  }

就是创建了一个虚拟的文本节点,然后执行_c方法:

vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); };

这个之前有提过,等执行完这些方法之后,我们就会有一个虚拟DOM节点,长这样:

QQ图片20190924202739.png


至此,vm._render执行完毕,代码最终返回到updateComponent方法里,开始执行,vm._update:

Vue.prototype._update = function (vnode, hydrating) {
      var vm = this;
      var prevEl = vm.$el;
      var prevVnode = vm._vnode;
      var restoreActiveInstance = setActiveInstance(vm);
      vm._vnode = vnode;
      // Vue.prototype.__patch__ is injected in entry points
      // based on the rendering backend used.
      if (!prevVnode) {
        // initial render
        vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */);
      } else {
        // updates
        vm.$el = vm.__patch__(prevVnode, vnode);
      }
      restoreActiveInstance();
      // update __vue__ reference
      if (prevEl) {
        prevEl.__vue__ = null;
      }
      if (vm.$el) {
        vm.$el.__vue__ = vm;
      }
      // if parent is an HOC, update its $el as well
      if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
        vm.$parent.$el = vm.$el;
      }
      // updated hook is called by the scheduler to ensure that children are
      // updated in a parent's updated hook.
    };

和其他函数一样一开始先拿一些所需用到的变量,这里因为是第一次挂载,prevVnode为undefined,__patch__方法执行remove移除策略,代码注释中也有讲到:

function patch (oldVnode, vnode, hydrating, removeOnly) {
      if (isUndef(vnode)) {
        if (isDef(oldVnode)) { invokeDestroyHook(oldVnode); }
        return
      }

      var isInitialPatch = false;
      var insertedVnodeQueue = [];

      if (isUndef(oldVnode)) {
        // empty mount (likely as component), create new root element
        isInitialPatch = true;
        createElm(vnode, insertedVnodeQueue);
      } else {
        var isRealElement = isDef(oldVnode.nodeType);
        if (!isRealElement && sameVnode(oldVnode, vnode)) {
          // patch existing root node
          patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly);
        } else {
          if (isRealElement) {
            // mounting to a real element
            // check if this is server-rendered content and if we can perform
            // a successful hydration.
            if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) {
              oldVnode.removeAttribute(SSR_ATTR);
              hydrating = true;
            }
            if (isTrue(hydrating)) {
              if (hydrate(oldVnode, vnode, insertedVnodeQueue)) {
                invokeInsertHook(vnode, insertedVnodeQueue, true);
                return oldVnode
              } else {
                warn(
                  'The client-side rendered virtual DOM tree is not matching ' +
                  'server-rendered content. This is likely caused by incorrect ' +
                  'HTML markup, for example nesting block-level elements inside ' +
                  '<p>, or missing <tbody>. Bailing hydration and performing ' +
                  'full client-side render.'
                );
              }
            }
            // either not server-rendered, or hydration failed.
            // create an empty node and replace it
            oldVnode = emptyNodeAt(oldVnode);
          }

          // replacing existing element
          var oldElm = oldVnode.elm;
          var parentElm = nodeOps.parentNode(oldElm);

          // create new node
          createElm(
            vnode,
            insertedVnodeQueue,
            // extremely rare edge case: do not insert if old element is in a
            // leaving transition. Only happens when combining transition +
            // keep-alive + HOCs. (#4590)
            oldElm._leaveCb ? null : parentElm,
            nodeOps.nextSibling(oldElm)
          );

          // update parent placeholder node element, recursively
          if (isDef(vnode.parent)) {
            var ancestor = vnode.parent;
            var patchable = isPatchable(vnode);
            while (ancestor) {
              for (var i = 0; i < cbs.destroy.length; ++i) {
                cbs.destroy[i](ancestor);
              }
              ancestor.elm = vnode.elm;
              if (patchable) {
                for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) {
                  cbs.create[i$1](emptyNode, ancestor);
                }
                // #6513
                // invoke insert hooks that may have been merged by create hooks.
                // e.g. for directives that uses the "inserted" hook.
                var insert = ancestor.data.hook.insert;
                if (insert.merged) {
                  // start at index 1 to avoid re-invoking component mounted hook
                  for (var i$2 = 1; i$2 < insert.fns.length; i$2++) {
                    insert.fns[i$2]();
                  }
                }
              } else {
                registerRef(ancestor);
              }
              ancestor = ancestor.parent;
            }
          }

          // destroy old node
          if (isDef(parentElm)) {
            removeVnodes(parentElm, [oldVnode], 0, 0);
          } else if (isDef(oldVnode.tag)) {
            invokeDestroyHook(oldVnode);
          }
        }
      }

      invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch);
      return vnode.elm
    }
  }

patch方法中,首先会初始化isRealElement变量,这里我们我们的oldVnode调用的时候直接传的是div的引用,故而为true,所以if-else分支走了最下面的一个,当然这是第一次挂载,当为更新的时候,isRealElement的值为false,就会执行patchVnode方法,去patch新旧Vnode, 这里注意,patch的时候,patchVnode,只会在两个vnode是相同的时候才会patch,而什么时候是两个相同的vnode呢?就是下面这个方法的返回值为真的时候:

function sameVnode (a, b) {
    return (
      a.key === b.key && (
        (
          a.tag === b.tag &&
          a.isComment === b.isComment &&
          isDef(a.data) === isDef(b.data) &&
          sameInputType(a, b)
        ) || (
          isTrue(a.isAsyncPlaceholder) &&
          a.asyncFactory === b.asyncFactory &&
          isUndef(b.asyncFactory.error)
        )
      )
    )
  }

所以看到key值得重要性了么。

继续到我们得patch得方法,接着会调emptyNodeAt,创造一个tag是div得空Vnode,然后将挂载上得div元素引用和body得引用拿到,使用createElm方法创建真实得dom节点:

function createElm (
      vnode,
      insertedVnodeQueue,
      parentElm,
      refElm,
      nested,
      ownerArray,
      index
    ) {
      if (isDef(vnode.elm) && isDef(ownerArray)) {
        // This vnode was used in a previous render!
        // now it's used as a new node, overwriting its elm would cause
        // potential patch errors down the road when it's used as an insertion
        // reference node. Instead, we clone the node on-demand before creating
        // associated DOM element for it.
        vnode = ownerArray[index] = cloneVNode(vnode);
      }

      vnode.isRootInsert = !nested; // for transition enter check
      if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
        return
      }

      var data = vnode.data;
      var children = vnode.children;
      var tag = vnode.tag;
      if (isDef(tag)) {
        {
          if (data && data.pre) {
            creatingElmInVPre++;
          }
          if (isUnknownElement$$1(vnode, creatingElmInVPre)) {
            warn(
              'Unknown custom element: <' + tag + '> - did you ' +
              'register the component correctly? For recursive components, ' +
              'make sure to provide the "name" option.',
              vnode.context
            );
          }
        }

        vnode.elm = vnode.ns
          ? nodeOps.createElementNS(vnode.ns, tag)
          : nodeOps.createElement(tag, vnode);
        setScope(vnode);

        /* istanbul ignore if */
        {
          createChildren(vnode, children, insertedVnodeQueue);
          if (isDef(data)) {
            invokeCreateHooks(vnode, insertedVnodeQueue);
          }
          insert(parentElm, vnode.elm, refElm);
        }

        if (data && data.pre) {
          creatingElmInVPre--;
        }
      } else if (isTrue(vnode.isComment)) {
        vnode.elm = nodeOps.createComment(vnode.text);
        insert(parentElm, vnode.elm, refElm);
      } else {
        vnode.elm = nodeOps.createTextNode(vnode.text);
        insert(parentElm, vnode.elm, refElm);
      }
    }

一进来得if是说如果当前得vnode在前一个render中,现在被当作一个新得vnode使用时,重写他的elm属性可能会引发后续得patch error,这个时候应该clone他。下来会使用createComponent方法做个检查,接着拿到vnode得data,children,tag,以上三个都是创建dom节点时需要用到的,setscope之后创建children节点,createChildren就是循环children依次调用createElm完成得,这里子节点因为是文本节点最后调用了nodeOps.createTextNode完成创建,并插入到父级得el上,然后会使用invokeCreateHooks更新dom属性,最后将完成得新的dom节点插入到body上。

执行完createElm方法后,就会将新生成得dom节点插入到dom树上,然后我们得代码继续走,返回到patch方法中,移除原来得dom节点。然后patch执行完毕,返回到Vue.prototype._update方法中,执行一些标记操作,然后继续返回,直到返回到Watcher.prototype.get中,接下来执行popTarget方法,将当前watcher实例从targetStack中拿出来,并将dep.target设置为undefined,这也预示着此次依赖收集已经结束,接着执行Watcher.prototype.cleanupDeps重新初始化deps属性,然后回到依赖收集最开始得那条语句,也就是watcher得构造函数里的:

this.value = this.lazy
      ? undefined
      : this.get();

至此,dom挂载结束,依赖收集完毕,然后回到mountComponent方法,唤起mounted生命周期钩子,层层返回。然后源码版hello wrold至此结束。随后还会有一个异步代码,唤起devtools,这是后话。


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