• Vue源码后记-vFor列表渲染(3)


      这一节肯定能完!

      

      经过DOM字符串的AST转化,再通过render变成vnode,最后就剩下patch到页面上了。

      render函数跑完应该是在这里:

        function mountComponent(vm, el, hydrating) {
            vm.$el = el;
            if (!vm.$options.render) {
                vm.$options.render = createEmptyVNode; {
                    // warning
                }
            }
            // beforeMount
    
            var updateComponent;
            /* istanbul ignore if */
            if ("development" !== 'production' && config.performance && mark) {
                updateComponent = function() {
                    // dev render
                };
            } else {
                updateComponent = function() {
                    vm._update(vm._render(), hydrating);
                };
            }
    
            // code...
    
            // mounted
            return vm
        }

      vm._render()会生成一个vnode看,接下来调用_update渲染页面,如下:

        Vue.prototype._update = function(vnode, hydrating) {
            var vm = this;
            // beforeUpdate
    
            // code...
    
            if (!prevVnode) {
                // initial render
                vm.$el = vm.__patch__(
                    vm.$el, vnode, hydrating, false /* removeOnly */ ,
                    vm.$options._parentElm,
                    vm.$options._refElm
                );
            } else {
                // updates
                vm.$el = vm.__patch__(prevVnode, vnode);
            }
    
            // code...
        };
    
        Vue$3.prototype.__patch__ = inBrowser ? patch : noop;
    
        var patch = createPatchFunction({
            nodeOps: nodeOps,
            modules: modules
        });
    
        function createPatchFunction(backend) {
            var i, j;
            var cbs = {};
    
            var modules = backend.modules;
            var nodeOps = backend.nodeOps;
    
            for (i = 0; i < hooks.length; ++i) {
                // hook...
            }
    
            // fn...
    
            return function patch(oldVnode, vnode, hydrating, removeOnly, parentElm, refElm) {
                // code...
    
                if (isUndef(oldVnode)) {
                    // component...
                } else {
                    var isRealElement = isDef(oldVnode.nodeType);
                    if (!isRealElement && sameVnode(oldVnode, vnode)) {
                        // patch existing root node
                    } else {
                        // SSR or hydrating
    
                        var oldElm = oldVnode.elm;
                        var parentElm$1 = nodeOps.parentNode(oldElm);
                        createElm(
                            vnode,
                            insertedVnodeQueue,
                            oldElm._leaveCb ? null : parentElm$1,
                            nodeOps.nextSibling(oldElm)
                        );
    
                        //code...
                    }
                }
    
                invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch);
                return vnode.elm
            }
        }

      由于是初始化页面,所有在update的过程中,oldVNode被设置为空的div虚拟DOM,然后与生成的虚拟DOM进行替换。

      核心细节在上述代码中的createElm函数:

        // vnode => 生成的vnode
        // insertedVnodeQueue => []
        // parentElm => body
        // refElm => #text
        // nested => undefined
        function createElm(vnode, insertedVnodeQueue, parentElm, refElm, nested) {
            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)) {
                // pre...
    
                vnode.elm = vnode.ns ?
                    nodeOps.createElementNS(vnode.ns, tag) :
                    // 调用这个生成一个tag标签
                    nodeOps.createElement(tag, vnode);
                setScope(vnode);
    
                {
                    // 处理子节点
                    // 子节点是5个vnode组成的数据 因此会循环调用本函数
                    createChildren(vnode, children, insertedVnodeQueue);
                    if (isDef(data)) {
                        // 生成DOM节点的属性
                        invokeCreateHooks(vnode, insertedVnodeQueue);
                    }
                    // 将子节点插入到父节点中
                    // 处理到最外层节点 页面会渲染
                    insert(parentElm, vnode.elm, refElm);
                }
    
                if ("development" !== 'production' && data && data.pre) {
                    inPre--;
                }
            } 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);
            }
        }

      其实,这个普通的patch没有区别,只是由于是多个标签,所以会有兄弟元素,在插入节点会调用insertBefore进行插入,最后5个a标签依次插入生成的div,然后div插入body标签完成页面渲染。

      虽然循环生成a标签以及其属性比较麻烦,但是由于整个标签是一次性插入body中,所以对于性能也没有什么影响。

      完事,确实没什么好说的,至于v-if、v-show那些,有空一次性写完。

  • 相关阅读:
    发布Web端
    回溯算法套路详解(转)
    埋点自动化测试框架设计
    Mitmproxy + Python 做拦截代理
    面向对象三大特性-多态的思考
    java集合类-Set接口
    java集合类-List接口
    java集合类-集合框架体系
    测试的Python、 Java语言之争
    Python迭代器、生成器
  • 原文地址:https://www.cnblogs.com/QH-Jimmy/p/7283488.html
Copyright © 2020-2023  润新知