摘要:前言在上一章我們學(xué)習(xí)了,等模塊,在這一篇我們將會學(xué)習(xí)到的核心功能和功能。如果父節(jié)點沒變化,我們就比較所有同層的子節(jié)點,對這些子節(jié)點進行刪除創(chuàng)建移位操作。只需要對兩個進行判斷是否相似,如果相似,則對他們進行操作,否則直接用替換。
前言
在上一章我們學(xué)習(xí)了,modules,vnode,h,htmldomapi,is等模塊,在這一篇我們將會學(xué)習(xí)到
snabbdom的核心功能——patchVnode和updateChildren功能。
首先我們先從簡單的部分開始,比如一些工具函數(shù),我將逐個來講解他們的用處
sameNode這個函數(shù)主要用于比較oldvnode與vnode同層次節(jié)點的比較,如果同層次節(jié)點的key和sel都相同
我們就可以保留這個節(jié)點,否則直接替換節(jié)點
function sameVnode(vnode1, vnode2) { return vnode1.key === vnode2.key && vnode1.sel === vnode2.sel; }createKeyToOldIdx
這個函數(shù)的功能十分簡單,就是將oldvnode數(shù)組中位置對oldvnode.key的映射轉(zhuǎn)換為oldvnode.key
對位置的映射
function createKeyToOldIdx(children, beginIdx, endIdx) { var i, map = {}, key; for (i = beginIdx; i <= endIdx; ++i) { key = children[i].key; if (isDef(key)) map[key] = i; } return map; }hook
snabbdom在全局下有6種類型的鉤子,觸發(fā)這些鉤子時,會調(diào)用對應(yīng)的函數(shù)對節(jié)點的狀態(tài)進行更改
首先我們來看看有哪些鉤子:
Name | Triggered when | Arguments to callback |
---|---|---|
pre | the patch process begins (patch開始時觸發(fā)) | none |
init | a vnode has been added (vnode被創(chuàng)建時觸發(fā)) | vnode |
create | a DOM element has been created based on a vnode (vnode轉(zhuǎn)換為真實DOM節(jié)點時觸發(fā) | emptyVnode, vnode |
insert | an element has been inserted into the DOM (插入到DOM樹時觸發(fā)) | vnode |
prepatch | an element is about to be patched (元素準(zhǔn)備patch前觸發(fā)) | oldVnode, vnode |
update | an element is being updated (元素更新時觸發(fā)) | oldVnode, vnode |
postpatch | an element has been patched (元素patch完觸發(fā)) | oldVnode, vnode |
destroy | an element is directly or indirectly being removed (元素被刪除時觸發(fā)) | vnode |
remove | an element is directly being removed from the DOM (元素從父節(jié)點刪除時觸發(fā),和destory略有不同,remove只影響到被移除節(jié)點中最頂層的節(jié)點) | vnode, removeCallback |
post | the patch process is done (patch完成后觸發(fā)) | none |
然后,下面列出鉤子對應(yīng)的狀態(tài)更新函數(shù):
create => style,class,dataset,eventlistener,props,hero
update => style,class,dataset,eventlistener,props,hero
remove => style
destory => eventlistener,style,hero
pre => hero
post => hero
好了,簡單的都看完了,接下來我們開始打大boss了,第一關(guān)就是init函數(shù)了
initinit函數(shù)有兩個參數(shù)modules和api,其中modules是init依賴的模塊,如attribute、props
、eventlistener這些模塊,api則是對封裝真實DOM操作的工具函數(shù)庫,如果我們沒有傳入,則默認(rèn)
使用snabbdom提供的htmldomapi。init還包含了許多vnode和真實DOM之間的操作和注冊全局鉤子,
還有patchVnode和updateChildren這兩個重要功能,然后返回一個patch函數(shù)
//注冊鉤子的回調(diào),在發(fā)生狀態(tài)變更時,觸發(fā)對應(yīng)屬性變更 for (i = 0; i < hooks.length; ++i) { cbs[hooks[i]] = []; for (j = 0; j < modules.length; ++j) { if (modules[j][hooks[i]] !== undefined) cbs[hooks[i]].push(modules[j][hooks[i]]); } }emptyNodeAt
這個函數(shù)主要的功能是將一個真實DOM節(jié)點轉(zhuǎn)化成vnode形式, 我們知道當(dāng)我們需要remove一個vnode時,會觸發(fā)remove鉤子作攔截器,只有在所有remove鉤子 這個函數(shù)用于手動觸發(fā)destory鉤子回調(diào),主要步驟如下: 先調(diào)用vnode上的destory 再調(diào)用全局下的destory
遞歸調(diào)用子vnode的destory 這個函數(shù)主要功能是批量刪除DOM節(jié)點,需要配合invokeDestoryHook和createRmCb服用,效果更佳 調(diào)用invokeDestoryHook以觸發(fā)destory回調(diào) 調(diào)用createRmCb來開始對remove回調(diào)進行計數(shù)
刪除DOM節(jié)點 就如太極有陰就有陽一樣,既然我們有remove操作,肯定也有createelm的操作,這個函數(shù)主要功能 初始化vnode,調(diào)用init鉤子 創(chuàng)建對應(yīng)tagname的DOM element節(jié)點,并將vnode.sel中的id名和class名掛載上去
如果有子vnode,遞歸創(chuàng)建DOM element節(jié)點,并添加到父vnode對應(yīng)的element節(jié)點上去, vnode轉(zhuǎn)換成dom節(jié)點操作完成后,調(diào)用create鉤子
如果vnode上有insert鉤子,那么就將這個vnode放入insertedVnodeQueue中作記錄,到時 這個函數(shù)十分簡單,就是將vnode轉(zhuǎn)換后的dom節(jié)點插入到dom樹的指定位置中去 說完上面的節(jié)點工具函數(shù)之后,我們就開始看如何進行patch操作了,首先我們從patch,也就是init 首先我們需要明確的一個是,如果按照傳統(tǒng)的diff算法,那么為了找到最小變化,需要逐層逐層的去 真正對vnode內(nèi)部patch的還是得靠patchVnode。讓我們看看他到底做了什么? 對于同層的子節(jié)點,snabbdom主要有刪除、創(chuàng)建的操作,同時通過移位的方法,達(dá)到最大復(fù)用存在 oldStartIdx => 舊頭索引 oldEndIdx => 舊尾索引 newStartIdx => 新頭索引 newEndIdx => 新尾索引 然后開始將舊子節(jié)點組和新子節(jié)點組進行逐一比對,直到遍歷完任一子節(jié)點組,比對策略有5種: oldStartVnode和newStartVnode進行比對,如果相似,則進行patch,然后新舊頭索引都后移 oldEndVnode和newEndVnode進行比對,如果相似,則進行patch,然后新舊尾索引前移
oldStartVnode和newEndVnode進行比對,如果相似,則進行patch,將舊節(jié)點移位到最后 oldEndVnode和newStartVnode進行比對,處理和上面類似,只不過改為左移
如果以上情況都失敗了,我們就只能復(fù)用key相同的節(jié)點了。首先我們要通過createKeyToOldIdx 遍歷完之后,將剩余的新Vnode添加到最后一個新節(jié)點的位置后或者刪除多余的舊節(jié)點 至此,snabbdom的主要功能就分析完了 文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。 轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/88233.html
如將轉(zhuǎn)換為{sel:"div#a.b.c",data:{},children:[],text:undefined,elm: function emptyNodeAt(elm) {
var id = elm.id ? "#" + elm.id : "";
var c = elm.className ? "." + elm.className.split(" ").join(".") : "";
return VNode(api.tagName(elm).toLowerCase() + id + c, {}, [], undefined, elm);
}
createRmCb
回調(diào)函數(shù)都觸發(fā)完才會將節(jié)點從父節(jié)點刪除,而這個函數(shù)提供的就是對remove鉤子回調(diào)操作的計數(shù)功能function createRmCb(childElm, listeners) {
return function() {
if (--listeners === 0) {
var parent = api.parentNode(childElm);
api.removeChild(parent, childElm);
}
};
}
invokeDestoryHook
function invokeDestroyHook(vnode) {
var i, j, data = vnode.data;
if (isDef(data)) {
//先觸發(fā)該節(jié)點上的destory回調(diào)
if (isDef(i = data.hook) && isDef(i = i.destroy)) i(vnode);
//在觸發(fā)全局下的destory回調(diào)
for (i = 0; i < cbs.destroy.length; ++i) cbs.destroy[i](vnode);
//遞歸觸發(fā)子節(jié)點的destory回調(diào)
if (isDef(i = vnode.children)) {
for (j = 0; j < vnode.children.length; ++j) {
invokeDestroyHook(vnode.children[j]);
}
}
}
}
removeVnodes
主要步驟如下: /**
*
* @param parentElm 父節(jié)點
* @param vnodes 刪除節(jié)點數(shù)組
* @param startIdx 刪除起始坐標(biāo)
* @param endIdx 刪除結(jié)束坐標(biāo)
*/
function removeVnodes(parentElm, vnodes, startIdx, endIdx) {
for (; startIdx <= endIdx; ++startIdx) {
var i, listeners, rm, ch = vnodes[startIdx];
if (isDef(ch)) {
if (isDef(ch.sel)) {
//調(diào)用destroy鉤子
invokeDestroyHook(ch);
//對全局remove鉤子進行計數(shù)
listeners = cbs.remove.length + 1;
rm = createRmCb(ch.elm, listeners);
//調(diào)用全局remove回調(diào)函數(shù),并每次減少一個remove鉤子計數(shù)
for (i = 0; i < cbs.remove.length; ++i) cbs.remove[i](ch, rm);
//調(diào)用內(nèi)部vnode.data.hook中的remove鉤子(只有一個)
if (isDef(i = ch.data) && isDef(i = i.hook) && isDef(i = i.remove)) {
i(ch, rm);
} else {
//如果沒有內(nèi)部remove鉤子,需要調(diào)用rm,確保能夠remove節(jié)點
rm();
}
} else { // Text node
api.removeChild(parentElm, ch.elm);
}
}
}
}
createElm
如下:否則如果有text屬性,則創(chuàng)建text節(jié)點,并添加到父vnode對應(yīng)的element節(jié)點上去
再在全局批量調(diào)用insert鉤子回調(diào)
function createElm(vnode, insertedVnodeQueue) {
var i, data = vnode.data;
if (isDef(data)) {
//當(dāng)節(jié)點上存在hook而且hook中有init鉤子時,先調(diào)用init回調(diào),對剛創(chuàng)建的vnode進行處理
if (isDef(i = data.hook) && isDef(i = i.init)) {
i(vnode);
//獲取init鉤子修改后的數(shù)據(jù)
data = vnode.data;
}
}
var elm, children = vnode.children, sel = vnode.sel;
if (isDef(sel)) {
// Parse selector
var hashIdx = sel.indexOf("#");
//先id后class
var dotIdx = sel.indexOf(".", hashIdx);
var hash = hashIdx > 0 ? hashIdx : sel.length;
var dot = dotIdx > 0 ? dotIdx : sel.length;
var tag = hashIdx !== -1 || dotIdx !== -1 ? sel.slice(0, Math.min(hash, dot)) : sel;
//創(chuàng)建一個DOM節(jié)點引用,并對其屬性實例化
elm = vnode.elm = isDef(data) && isDef(i = data.ns) ? api.createElementNS(i, tag): api.createElement(tag);
//獲取id名 #a --> a
if (hash < dot) elm.id = sel.slice(hash + 1, dot);
//獲取類名,并格式化 .a.b --> a b
if (dotIdx > 0) elm.className = sel.slice(dot + 1).replace(/./g, " ");
//如果存在子元素Vnode節(jié)點,則遞歸將子元素節(jié)點插入到當(dāng)前Vnode節(jié)點中,并將已插入的子元素節(jié)點在insertedVnodeQueue中作記錄
if (is.array(children)) {
for (i = 0; i < children.length; ++i) {
api.appendChild(elm, createElm(children[i], insertedVnodeQueue));
}
//如果存在子文本節(jié)點,則直接將其插入到當(dāng)前Vnode節(jié)點
} else if (is.primitive(vnode.text)) {
api.appendChild(elm, api.createTextNode(vnode.text));
}
//當(dāng)創(chuàng)建完畢后,觸發(fā)全局create鉤子回調(diào)
for (i = 0; i < cbs.create.length; ++i) cbs.create[i](emptyNode, vnode);
i = vnode.data.hook; // Reuse variable
if (isDef(i)) {
if (i.create) i.create(emptyNode, vnode);
//如果有insert鉤子,則推進insertedVnodeQueue中作記錄,從而實現(xiàn)批量插入觸發(fā)insert回調(diào)
if (i.insert) insertedVnodeQueue.push(vnode);
}
}
//如果沒聲明選擇器,則說明這個是一個text節(jié)點
else {
elm = vnode.elm = api.createTextNode(vnode.text);
}
return vnode.elm;
}
addVnodes
function addVnodes(parentElm, before, vnodes, startIdx, endIdx, insertedVnodeQueue) {
for (; startIdx <= endIdx; ++startIdx) {
api.insertBefore(parentElm, createElm(vnodes[startIdx], insertedVnodeQueue), before);
}
}
返回的函數(shù)開始
搜索比較,這樣時間復(fù)雜度將會達(dá)到 O(n^3)的級別,代價十分高,考慮到節(jié)點變化很少是跨層次的,
vdom采取的是一種簡化的思路,只比較同層節(jié)點,如果不同,那么即使該節(jié)點的子節(jié)點沒變化,我們
也不復(fù)用,直接將從父節(jié)點開始的子樹全部刪除,然后再重新創(chuàng)建節(jié)點添加到新的位置。如果父節(jié)點
沒變化,我們就比較所有同層的子節(jié)點,對這些子節(jié)點進行刪除、創(chuàng)建、移位操作。有了這個思想,
理解patch也十分簡單了。patch只需要對兩個vnode進行判斷是否相似,如果相似,則對他們進行
patchVnode操作,否則直接用vnode替換oldvnode。return function(oldVnode, vnode) {
var i, elm, parent;
//記錄被插入的vnode隊列,用于批觸發(fā)insert
var insertedVnodeQueue = [];
//調(diào)用全局pre鉤子
for (i = 0; i < cbs.pre.length; ++i) cbs.pre[i]();
//如果oldvnode是dom節(jié)點,轉(zhuǎn)化為oldvnode
if (isUndef(oldVnode.sel)) {
oldVnode = emptyNodeAt(oldVnode);
}
//如果oldvnode與vnode相似,進行更新
if (sameVnode(oldVnode, vnode)) {
patchVnode(oldVnode, vnode, insertedVnodeQueue);
} else {
//否則,將vnode插入,并將oldvnode從其父節(jié)點上直接刪除
elm = oldVnode.elm;
parent = api.parentNode(elm);
createElm(vnode, insertedVnodeQueue);
if (parent !== null) {
api.insertBefore(parent, vnode.elm, api.nextSibling(elm));
removeVnodes(parent, [oldVnode], 0, 0);
}
}
//插入完后,調(diào)用被插入的vnode的insert鉤子
for (i = 0; i < insertedVnodeQueue.length; ++i) {
insertedVnodeQueue[i].data.hook.insert(insertedVnodeQueue[i]);
}
//然后調(diào)用全局下的post鉤子
for (i = 0; i < cbs.post.length; ++i) cbs.post[i]();
//返回vnode用作下次patch的oldvnode
return vnode;
};
patchVnode
function patchVnode(oldVnode, vnode, insertedVnodeQueue) {
var i, hook;
//在patch之前,先調(diào)用vnode.data的prepatch鉤子
if (isDef(i = vnode.data) && isDef(hook = i.hook) && isDef(i = hook.prepatch)) {
i(oldVnode, vnode);
}
var elm = vnode.elm = oldVnode.elm, oldCh = oldVnode.children, ch = vnode.children;
//如果oldvnode和vnode的引用相同,說明沒發(fā)生任何變化直接返回,避免性能浪費
if (oldVnode === vnode) return;
//如果oldvnode和vnode不同,說明vnode有更新
//如果vnode和oldvnode不相似則直接用vnode引用的DOM節(jié)點去替代oldvnode引用的舊節(jié)點
if (!sameVnode(oldVnode, vnode)) {
var parentElm = api.parentNode(oldVnode.elm);
elm = createElm(vnode, insertedVnodeQueue);
api.insertBefore(parentElm, elm, oldVnode.elm);
removeVnodes(parentElm, [oldVnode], 0, 0);
return;
}
//如果vnode和oldvnode相似,那么我們要對oldvnode本身進行更新
if (isDef(vnode.data)) {
//首先調(diào)用全局的update鉤子,對vnode.elm本身屬性進行更新
for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode);
//然后調(diào)用vnode.data里面的update鉤子,再次對vnode.elm更新
i = vnode.data.hook;
if (isDef(i) && isDef(i = i.update)) i(oldVnode, vnode);
}
//如果vnode不是text節(jié)點
if (isUndef(vnode.text)) {
//如果vnode和oldVnode都有子節(jié)點
if (isDef(oldCh) && isDef(ch)) {
//當(dāng)Vnode和oldvnode的子節(jié)點不同時,調(diào)用updatechilren函數(shù),diff子節(jié)點
if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue);
}
//如果vnode有子節(jié)點,oldvnode沒子節(jié)點
else if (isDef(ch)) {
//oldvnode是text節(jié)點,則將elm的text清除
if (isDef(oldVnode.text)) api.setTextContent(elm, "");
//并添加vnode的children
addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue);
}
//如果oldvnode有children,而vnode沒children,則移除elm的children
else if (isDef(oldCh)) {
removeVnodes(elm, oldCh, 0, oldCh.length - 1);
}
//如果vnode和oldvnode都沒chidlren,且vnode沒text,則刪除oldvnode的text
else if (isDef(oldVnode.text)) {
api.setTextContent(elm, "");
}
}
//如果oldvnode的text和vnode的text不同,則更新為vnode的text
else if (oldVnode.text !== vnode.text) {
api.setTextContent(elm, vnode.text);
}
//patch完,觸發(fā)postpatch鉤子
if (isDef(hook) && isDef(i = hook.postpatch)) {
i(oldVnode, vnode);
}
}
updateChildren
節(jié)點的目的,其中需要維護四個索引,分別是:然后舊頭索引后移,尾索引前移,為什么要這樣做呢?我們思考一種情況,如舊節(jié)點為【5,1,2,3,4】
,新節(jié)點為【1,2,3,4,5】,如果缺乏這種判斷,意味著需要先將5->1,1->2,2->3,3->4,4->5五
次刪除插入操作,即使是有了key-index來復(fù)用,也會出現(xiàn)也會出現(xiàn)【5,1,2,3,4】->
【1,5,2,3,4】->【1,2,5,3,4】->【1,2,3,5,4】->【1,2,3,4,5】共4次操作,如果
有了這種判斷,我們只需要將5插入到舊尾索引后面即可,從而實現(xiàn)右移
創(chuàng)建key-index的映射,如果新節(jié)點在舊節(jié)點中不存在,我們將它插入到舊頭索引節(jié)點前,
然后新頭索引向后;如果新節(jié)點在就舊節(jié)點組中存在,先找到對應(yīng)的舊節(jié)點,然后patch,并將
舊節(jié)點組中對應(yīng)節(jié)點設(shè)置為undefined,代表已經(jīng)遍歷過了,不再遍歷,否則可能存在重復(fù)
插入的問題,最后將節(jié)點移位到舊頭索引節(jié)點之前,新頭索引向后
/**
*
* @param parentElm 父節(jié)點
* @param oldCh 舊節(jié)點數(shù)組
* @param newCh 新節(jié)點數(shù)組
* @param insertedVnodeQueue
*/
function updateChildren(parentElm, oldCh, newCh, insertedVnodeQueue) {
var oldStartIdx = 0, newStartIdx = 0;
var oldEndIdx = oldCh.length - 1;
var oldStartVnode = oldCh[0];
var oldEndVnode = oldCh[oldEndIdx];
var newEndIdx = newCh.length - 1;
var newStartVnode = newCh[0];
var newEndVnode = newCh[newEndIdx];
var oldKeyToIdx, idxInOld, elmToMove, before;
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
if (isUndef(oldStartVnode)) {
oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left
} else if (isUndef(oldEndVnode)) {
oldEndVnode = oldCh[--oldEndIdx];
}
//如果舊頭索引節(jié)點和新頭索引節(jié)點相同,
else if (sameVnode(oldStartVnode, newStartVnode)) {
//對舊頭索引節(jié)點和新頭索引節(jié)點進行diff更新, 從而達(dá)到復(fù)用節(jié)點效果
patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue);
//舊頭索引向后
oldStartVnode = oldCh[++oldStartIdx];
//新頭索引向后
newStartVnode = newCh[++newStartIdx];
}
//如果舊尾索引節(jié)點和新尾索引節(jié)點相似,可以復(fù)用
else if (sameVnode(oldEndVnode, newEndVnode)) {
//舊尾索引節(jié)點和新尾索引節(jié)點進行更新
patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue);
//舊尾索引向前
oldEndVnode = oldCh[--oldEndIdx];
//新尾索引向前
newEndVnode = newCh[--newEndIdx];
}
//如果舊頭索引節(jié)點和新頭索引節(jié)點相似,可以通過移動來復(fù)用
//如舊節(jié)點為【5,1,2,3,4】,新節(jié)點為【1,2,3,4,5】,如果缺乏這種判斷,意味著
//那樣需要先將5->1,1->2,2->3,3->4,4->5五次刪除插入操作,即使是有了key-index來復(fù)用,
// 也會出現(xiàn)【5,1,2,3,4】->【1,5,2,3,4】->【1,2,5,3,4】->【1,2,3,5,4】->【1,2,3,4,5】
// 共4次操作,如果有了這種判斷,我們只需要將5插入到最后一次操作即可
else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue);
api.insertBefore(parentElm, oldStartVnode.elm, api.nextSibling(oldEndVnode.elm));
oldStartVnode = oldCh[++oldStartIdx];
newEndVnode = newCh[--newEndIdx];
}
//原理與上面相同
else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue);
api.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);
oldEndVnode = oldCh[--oldEndIdx];
newStartVnode = newCh[++newStartIdx];
}
//如果上面的判斷都不通過,我們就需要key-index表來達(dá)到最大程度復(fù)用了
else {
//如果不存在舊節(jié)點的key-index表,則創(chuàng)建
if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx);
//找到新節(jié)點在舊節(jié)點組中對應(yīng)節(jié)點的位置
idxInOld = oldKeyToIdx[newStartVnode.key];
//如果新節(jié)點在舊節(jié)點中不存在,我們將它插入到舊頭索引節(jié)點前,然后新頭索引向后
if (isUndef(idxInOld)) { // New element
api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm);
newStartVnode = newCh[++newStartIdx];
} else {
//如果新節(jié)點在就舊節(jié)點組中存在,先找到對應(yīng)的舊節(jié)點
elmToMove = oldCh[idxInOld];
//先將新節(jié)點和對應(yīng)舊節(jié)點作更新
patchVnode(elmToMove, newStartVnode, insertedVnodeQueue);
//然后將舊節(jié)點組中對應(yīng)節(jié)點設(shè)置為undefined,代表已經(jīng)遍歷過了,不在遍歷,否則可能存在重復(fù)插入的問題
oldCh[idxInOld] = undefined;
//插入到舊頭索引節(jié)點之前
api.insertBefore(parentElm, elmToMove.elm, oldStartVnode.elm);
//新頭索引向后
newStartVnode = newCh[++newStartIdx];
}
}
}
//當(dāng)舊頭索引大于舊尾索引時,代表舊節(jié)點組已經(jīng)遍歷完,將剩余的新Vnode添加到最后一個新節(jié)點的位置后
if (oldStartIdx > oldEndIdx) {
before = isUndef(newCh[newEndIdx+1]) ? null : newCh[newEndIdx+1].elm;
addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);
}
//如果新節(jié)點組先遍歷完,那么代表舊節(jié)點組中剩余節(jié)點都不需要,所以直接刪除
else if (newStartIdx > newEndIdx) {
removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx);
}
}
摘要:閑聊在學(xué)的過程中,虛擬應(yīng)該是聽的最多的概念之一,得知其是借鑒進行開發(fā),故習(xí)之。以我的觀點來看,多個相同元素渲染時,則需要為每個元素添加值。 閑聊:在學(xué)vue的過程中,虛擬dom應(yīng)該是聽的最多的概念之一,得知其是借鑒snabbdom.js進行開發(fā),故習(xí)之。由于我工作處于IE8的環(huán)境,對ES6,TS這些知識的練習(xí)也只是淺嘗輒止,而snabbdom.js從v.0.5.4這個版本后開始使用TS...
摘要:前言最近在學(xué)習(xí)的源碼,剛開始看其源碼,著實找不到方向,因為其在的實現(xiàn)上還加入了很多本身的鉤子,加大了閱讀難度。 前言 最近在學(xué)習(xí)vue2.0的源碼,剛開始看其vdom源碼,著實找不到方向,因為其在vdom的實現(xiàn)上還加入了很多vue2.0本身的鉤子,加大了閱讀難度。于是看到第一行尤大說vue2.0的vdom是在snabbdom的基礎(chǔ)上改過來的,而snabbdom只有不到300sloc,那...
摘要:司徒正美的一款了不起的化方案,支持到。行代碼內(nèi)實現(xiàn)一個胡子大哈實現(xiàn)的作品其實就是的了源碼學(xué)習(xí)個人文章源碼學(xué)習(xí)個人文章源碼學(xué)習(xí)個人文章源碼學(xué)習(xí)個人文章這幾片文章的作者都是司徒正美,全面的解析和官方的對比。 前言 在過去的一個多月中,為了能夠更深入的學(xué)習(xí),使用React,了解React內(nèi)部算法,數(shù)據(jù)結(jié)構(gòu),我自己,從零開始寫了一個玩具框架。 截止今日,終于可以發(fā)布第一個版本,因為就在昨天,我...
摘要:那個率先改變的實例的返回值,就會傳遞給的回調(diào)函數(shù)。函數(shù)對函數(shù)的改進,體現(xiàn)在以下四點內(nèi)置執(zhí)行器。進一步說,函數(shù)完全可以看作多個異步操作,包裝成的一個對象,而命令就是內(nèi)部命令的語法糖。中的本質(zhì)就是沒有的隱藏的組件。 1、原型 - jquery使用showImg(https://segmentfault.com/img/bVbwNcY?w=692&h=442);注釋 : 實例雖然不同,但是構(gòu)...
閱讀 533·2023-04-26 01:39
閱讀 4485·2021-11-16 11:45
閱讀 2610·2021-09-27 13:37
閱讀 882·2021-09-01 10:50
閱讀 3579·2021-08-16 10:50
閱讀 2217·2019-08-30 15:55
閱讀 2979·2019-08-30 15:55
閱讀 2259·2019-08-30 14:07