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

vue

  这一节肯定能完!

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

以上是 Vue源码后记-vFor列表渲染(3) 的全部内容, 来源链接: utcz.com/z/378044.html

回到顶部