4.4 虚拟Vnode映射成真实DOM

回到 updateComponent的最后一个过程,虚拟的DOM树在生成virtual dom后,会调用Vue原型上_update方法,将虚拟DOM映射成为真实的DOM。从源码上可以知道,_update的调用时机有两个,一个是发生在初次渲染阶段,另一个发生数据更新阶段。

  1. updateComponent = function () {
  2. // render生成虚拟DOM,update渲染真实DOM
  3. vm._update(vm._render(), hydrating);
  4. };

vm._update方法的定义在lifecycleMixin中。

  1. lifecycleMixin()
  2. function lifecycleMixin() {
  3. Vue.prototype._update = function (vnode, hydrating) {
  4. var vm = this;
  5. var prevEl = vm.$el;
  6. var prevVnode = vm._vnode; // prevVnode为旧vnode节点
  7. // 通过是否有旧节点判断是初次渲染还是数据更新
  8. if (!prevVnode) {
  9. // 初次渲染
  10. vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false)
  11. } else {
  12. // 数据更新
  13. vm.$el = vm.__patch__(prevVnode, vnode);
  14. }
  15. }

_update的核心是__patch__方法,如果是服务端渲染,由于没有DOM_patch方法是一个空函数,在有DOM对象的浏览器环境下,__patch__patch函数的引用。

  1. // 浏览器端才有DOM,服务端没有dom,所以patch为一个空函数
  2. Vue.prototype.__patch__ = inBrowser ? patch : noop;

patch方法又是createPatchFunction方法的返回值,createPatchFunction方法传递一个对象作为参数,对象拥有两个属性,nodeOpsmodulesnodeOps封装了一系列操作原生DOM对象的方法。而modules定义了模块的钩子函数。

  1. var patch = createPatchFunction({ nodeOps: nodeOps, modules: modules });
  2. // 将操作dom对象的方法合集做冻结操作
  3. var nodeOps = /*#__PURE__*/Object.freeze({
  4. createElement: createElement$1,
  5. createElementNS: createElementNS,
  6. createTextNode: createTextNode,
  7. createComment: createComment,
  8. insertBefore: insertBefore,
  9. removeChild: removeChild,
  10. appendChild: appendChild,
  11. parentNode: parentNode,
  12. nextSibling: nextSibling,
  13. tagName: tagName,
  14. setTextContent: setTextContent,
  15. setStyleScope: setStyleScope
  16. });
  17. // 定义了模块的钩子函数
  18. var platformModules = [
  19. attrs,
  20. klass,
  21. events,
  22. domProps,
  23. style,
  24. transition
  25. ];
  26. var modules = platformModules.concat(baseModules);

真正的createPatchFunction函数有一千多行代码,这里就不方便列举出来了,它的内部首先定义了一系列辅助的方法,而核心是通过调用createElm方法进行dom操作,创建节点,插入子节点,递归创建一个完整的DOM树并插入到Body中。并且在产生真实阶段阶段,会有diff算法来判断前后Vnode的差异,以求最小化改变真实阶段。后面会有一个章节的内容去讲解diff算法。createPatchFunction的过程只需要先记住一些结论,函数内部会调用封装好的DOM api,根据Virtual DOM的结果去生成真实的节点。其中如果遇到组件Vnode时,会递归调用子组件的挂载过程,这个过程我们也会放到后面章节去分析。