国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

vue2源碼學(xué)習(xí)開胃菜——snabbdom源碼學(xué)習(xí)(二)

BetaRabbit / 1136人閱讀

摘要:前言在上一章我們學(xué)習(xí)了,等模塊,在這一篇我們將會學(xué)習(xí)到的核心功能和功能。如果父節(jié)點沒變化,我們就比較所有同層的子節(jié)點,對這些子節(jié)點進行刪除創(chuàng)建移位操作。只需要對兩個進行判斷是否相似,如果相似,則對他們進行操作,否則直接用替換。

前言

在上一章我們學(xué)習(xí)了,modules,vnode,h,htmldomapi,is等模塊,在這一篇我們將會學(xué)習(xí)到
snabbdom的核心功能——patchVnode和updateChildren功能。

繼續(xù)我們的snabbdom源碼之旅 最終章 snabbdom!

首先我們先從簡單的部分開始,比如一些工具函數(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ù)了

init

init函數(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形式,

將轉(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

我們知道當(dāng)我們需要remove一個vnode時,會觸發(fā)remove鉤子作攔截器,只有在所有remove鉤子
回調(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

這個函數(shù)用于手動觸發(fā)destory鉤子回調(diào),主要步驟如下:

先調(diào)用vnode上的destory

再調(diào)用全局下的destory

遞歸調(diào)用子vnode的destory

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

這個函數(shù)主要功能是批量刪除DOM節(jié)點,需要配合invokeDestoryHook和createRmCb服用,效果更佳
主要步驟如下:

調(diào)用invokeDestoryHook以觸發(fā)destory回調(diào)

調(diào)用createRmCb來開始對remove回調(diào)進行計數(shù)

刪除DOM節(jié)點

  /**
   *
   * @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

就如太極有陰就有陽一樣,既然我們有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é)點上去,

否則如果有text屬性,則創(chuàng)建text節(jié)點,并添加到父vnode對應(yīng)的element節(jié)點上去

vnode轉(zhuǎn)換成dom節(jié)點操作完成后,調(diào)用create鉤子

如果vnode上有insert鉤子,那么就將這個vnode放入insertedVnodeQueue中作記錄,到時

再在全局批量調(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

這個函數(shù)十分簡單,就是將vnode轉(zhuǎn)換后的dom節(jié)點插入到dom樹的指定位置中去

function addVnodes(parentElm, before, vnodes, startIdx, endIdx, insertedVnodeQueue) {
    for (; startIdx <= endIdx; ++startIdx) {
      api.insertBefore(parentElm, createElm(vnodes[startIdx], insertedVnodeQueue), before);
    }
  }

說完上面的節(jié)點工具函數(shù)之后,我們就開始看如何進行patch操作了,首先我們從patch,也就是init
返回的函數(shù)開始

patch

首先我們需要明確的一個是,如果按照傳統(tǒng)的diff算法,那么為了找到最小變化,需要逐層逐層的去
搜索比較,這樣時間復(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

真正對vnode內(nèi)部patch的還是得靠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é)點,snabbdom主要有刪除、創(chuàng)建的操作,同時通過移位的方法,達(dá)到最大復(fù)用存在
節(jié)點的目的,其中需要維護四個索引,分別是:

oldStartIdx => 舊頭索引

oldEndIdx => 舊尾索引

newStartIdx => 新頭索引

newEndIdx => 新尾索引

然后開始將舊子節(jié)點組和新子節(jié)點組進行逐一比對,直到遍歷完任一子節(jié)點組,比對策略有5種:

oldStartVnode和newStartVnode進行比對,如果相似,則進行patch,然后新舊頭索引都后移

oldEndVnode和newEndVnode進行比對,如果相似,則進行patch,然后新舊尾索引前移

oldStartVnode和newEndVnode進行比對,如果相似,則進行patch,將舊節(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)右移

oldEndVnode和newStartVnode進行比對,處理和上面類似,只不過改為左移

如果以上情況都失敗了,我們就只能復(fù)用key相同的節(jié)點了。首先我們要通過createKeyToOldIdx

創(chuàng)建key-index的映射,如果新節(jié)點在舊節(jié)點中不存在,我們將它插入到舊頭索引節(jié)點前,
然后新頭索引向后;如果新節(jié)點在就舊節(jié)點組中存在,先找到對應(yīng)的舊節(jié)點,然后patch,并將
舊節(jié)點組中對應(yīng)節(jié)點設(shè)置為undefined,代表已經(jīng)遍歷過了,不再遍歷,否則可能存在重復(fù)
插入的問題,最后將節(jié)點移位到舊頭索引節(jié)點之前,新頭索引向后

遍歷完之后,將剩余的新Vnode添加到最后一個新節(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);
    }
  }

至此,snabbdom的主要功能就分析完了

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/88233.html

相關(guān)文章

  • Snabbdom.js(一)

    摘要:閑聊在學(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...

    mating 評論0 收藏0
  • vue2源碼學(xué)習(xí)開胃——snabbdom源碼學(xué)習(xí)(一)

    摘要:前言最近在學(xué)習(xí)的源碼,剛開始看其源碼,著實找不到方向,因為其在的實現(xiàn)上還加入了很多本身的鉤子,加大了閱讀難度。 前言 最近在學(xué)習(xí)vue2.0的源碼,剛開始看其vdom源碼,著實找不到方向,因為其在vdom的實現(xiàn)上還加入了很多vue2.0本身的鉤子,加大了閱讀難度。于是看到第一行尤大說vue2.0的vdom是在snabbdom的基礎(chǔ)上改過來的,而snabbdom只有不到300sloc,那...

    betacat 評論0 收藏0
  • Luy 1.0 :一個React-like輪子的誕生

    摘要:司徒正美的一款了不起的化方案,支持到。行代碼內(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ā)布第一個版本,因為就在昨天,我...

    codecook 評論0 收藏0
  • javascript高級學(xué)習(xí)總結(jié)(

    摘要:那個率先改變的實例的返回值,就會傳遞給的回調(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)...

    Songlcy 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<