• 4.4 虚拟Vnode映射成真实DOM

    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时,会递归调用子组件的挂载过程,这个过程我们也会放到后面章节去分析。