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那些,有空一次性写完。

时间: 2024-10-09 06:09:35

Vue源码后记-vFor列表渲染(3)的相关文章

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

这一节争取搞完! 回头来看看那个render代码,为了便于分析,做了更细致的注释: (function() { // 这里this指向vue对象 下面的所有方法默认调用Vue$3.prototype上的方法 with(this){ return _c/*方法调用 => has拦截器过滤*/ ('div',{attrs:{"id":"app"}}, _l/*方法调用 => has拦截器过滤*/( (items/*_data属性访问 => 自定义pro

Vue源码后记-钩子函数

vue源码的马拉松跑完了,可以放松一下写点小东西,其实源码讲20节都讲不完,跳了好多地方. 本人技术有限,无法跟大神一样,模拟vue手把手搭建一个MVVM框架,然后再分析原理,只能以门外汉的姿态简单过一下~ 想到什么写什么了,这节就简单说说钩子函数吧! vue中的钩子函数主要包含初始化的beforeCreated/created,Virtual Dom更新期间的beforeUpdate/updated,页面渲染期间的beforeMount/mounted,组件销毁期间的beforeDestroy

Vue源码后记-其余内置指令(2)

-- 指令这个讲起来还有点复杂,先把html弄上来: <body> <div id='app'> <div v-if="vIfIter" v-bind:style="styleObject"> <input v-show="vShowIter" v-model='vModel' /> <span v-once>{{msg}}</span> <div v-html=&qu

Vue源码探究-虚拟DOM的渲染

Vue源码探究-虚拟DOM的渲染 在虚拟节点的实现一篇中,除了知道了 VNode 类的实现之外,还简要地整理了一下DOM渲染的路径.在这一篇中,主要来分析一下两条路径的具体实现代码. 按照创建 Vue 实例后的一般执行流程,首先来看看实例初始化时对渲染模块的初始处理.这也是开始 mount 路径的前一步.初始包括两部分,一是向 Vue 类原型对象上挂载渲染相关的方法,而是初始化渲染相关的属性. 渲染的初始化 下面代码位于vue/src/core/instance/render.js 相关属性初始

[Vue源码]一起来学Vue模板编译原理(二)-AST生成Render字符串

本文我们一起通过学习Vue模板编译原理(二)-AST生成Render字符串来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫持和发布订阅 一起来学Vue模板编译原理(一)-Template生成AST 一起来学Vue模板编译原理(二)-AST生成Render字符串 一起来学Vue虚拟DOM解析-Virtual Dom实现和Dom-diff算法 这些文章统一放在我的git仓库:https://github.com/yzsunlei/javascri

vue源码之响应式数据

分析vue是如何实现数据响应的. 前记 现在回顾一下看数据响应的原因. 之前看了vuex和vue-i18n的源码, 他们都有自己内部的vm, 也就是vue实例. 使用的都是vue的响应式数据特性及$watchapi. 所以决定看一下vue的源码, 了解vue是如何实现响应式数据. 本文叙事方式为树藤摸瓜, 顺着看源码的逻辑走一遍, 查看的vue的版本为2.5.2. 目的 明确调查方向才能直至目标, 先说一下目标行为: vue中的数据改变, 视图层面就能获得到通知并进行渲染. $watchapi监

[Vue源码]一起来学Vue双向绑定原理-数据劫持和发布订阅

有一段时间没有更新技术博文了,因为这段时间埋下头来看Vue源码了.本文我们一起通过学习双向绑定原理来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫持和发布订阅 一起来学Vue模板编译原理(一)-Template生成AST 一起来学Vue模板编译原理(二)-AST生成Render字符串 一起来学Vue虚拟DOM解析-Virtual Dom实现和Dom-diff算法 这些文章统一放在我的git仓库:https://github.com/yzsun

[Vue源码]一起来学Vue模板编译原理(一)-Template生成AST

本文我们一起通过学习Vue模板编译原理(一)-Template生成AST来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫持和发布订阅 一起来学Vue模板编译原理(一)-Template生成AST 一起来学Vue模板编译原理(二)-AST生成Render字符串 一起来学Vue虚拟DOM解析-Virtual Dom实现和Dom-diff算法 这些文章统一放在我的git仓库:https://github.com/yzsunlei/javascrip

vue源码解读(一)Observer/Dep/Watcher是如何实现数据绑定的

欢迎star我的github仓库,共同学习~目前vue源码学习系列已经更新了5篇啦~ https://github.com/yisha0307/... 快速跳转: Vue的双向绑定原理(已完成) 说说vue中的Virtual DOM(已完成) React diff和Vue diff实现差别 Vue中的异步更新策略(已完成) Vuex的实现理解 Typescript学习笔记(持续更新ing) Vue源码中闭包的使用(已完成) 介绍 最近在学习vue和vuex的源码,记录自己的一些学习心得.主要借鉴