經過JS層面的計算(逐層比對虛擬DOM對象),生成patch對象(即補丁對象),而後對補丁進行解析和從新渲染node
- 每一層級進行對比的計算法則:深度優先遍歷
- 節點類型相同,看屬性是否相同,若是不一樣,則產生屬性的補丁包
{type:’ATTRS’,attrs:{class:’BB’}}
- 新的DOM節點不存在,被刪除了
{type:’REMOVE’,index:xxx}
- 節點類型不相同,直接替換便可
{type:’REPLACE’,newNode:xxx}
- 文本的內容進行變化
{type:’TEXT’,text:xxx}
…bash
function diff(oldTree,newTree){
let patchs = {}; //=>補丁包 按照層級放置補丁包 {1:[],2:[]}
let index = 0; //=>比較的層級
//=>遞歸樹,比較後的結果放到補丁包
walk(oldTree,newTree,index,patchs);
return patchs;
}
function walk(oldNode,newNode,index,patchs){
let currentPatch={};
if(!newNode){
//=>新元素不存在,表明刪除
currentPatch.push({
type:'EMOVE',
index
});
}else if(typeof oldNode==="string"&&typeof newNode==="string"){
//=>若是是文本,判斷文本是否改變
if(oldNode!==newNode){
currentPatch.push({
type:'TEXT',
text:newNode
});
}
}else if(oldNode.type===newNode.type){
//=>比較屬性是否有更改
let attrs=diffAttr(oldNode.props,newNode.props);,
if(Object.keys(attrs).length>0){
currentPatch.push({
type:'ATTRS',
attrs
});
}
//=>若是有兒子節點,則遍歷兒子
diffChildren(
oldNode.props.childrren,
newNode.props.childrren,
index,patchs);
}else{
//=>節點被替換了
currentPatch.push({
type:'REPACE',
newNode
});
}
//=>當前本級查找,確實有補丁,咱們放到最外層補丁包中
if(currentPatch.length>0){
patchs[index]=currentPatch;
}
}
//=>比較兒子
function diffChildren(oldChildren,newChildren,index,patchs){
oldChildrren.forEach((child,ind)=>{
walk(child,newChildren[ind],++index,patchs);
})
}
//=>比較屬性,生成補丁包
function diffAttr(oldAttrs,newAttrs){
let patch={};
for(ley key in oldAttrs){
//=>屬性不同(多是把老的中某個刪除了,這樣獲取的結果多是undefined)
if(oldAttrs[key]!==newAttrs[key]){
patch[key]=newAttrs[key];
}
}
for(ley key in newAttrs){
//=>看老的節點中是否有這樣一個屬性(沒有就是新增)
if(!oldAttrs.hasOwnProperty(key)){
patch[key]=newAttrs[key];
}
}
return patch;
}
複製代碼
let patchs=diff(xxx,xxx); //=>兩個虛擬DOM
let node;
let index=0;
walk(node);
functon walk(node){
let currentPatch=patches[index++];
let cildNodes=node.childNodes;
cildNodes.forEach(child=>walk(child));
if(currentPatch.length>0){
doPatch(node,currentPatch);
}
}
function doPatch(node,patch){
patchs.forRach((item,index)=>{
switch(patch.type){
case 'ATTRS':
for(let key in patch.attrs){
let val=patch.attrs[key];
if(val){
setAttr(node,key,val);
}else{
node.removeAttribute(key)
}
}
break;
case 'TEXT':
node.textContent=patch.text;
break;
case 'REPLACE':
let newNode=(patch.newNode instanceof Element)?render(patch.newNode):document.createTextNode(patch.newNode);
node.parentNode.replaceChild(newNode,node);
break;
case 'REMOVE':
node.parentNode.removeChild(node);
break;
}
})
}
複製代碼
- 第一次加載頁面,全部的內容都要從新渲染(語法解析 -> 虛擬DOM -> 真實DOM -> 瀏覽器渲染) =>第一次加載頁面越少渲染越好
- 上一次計算出來的虛擬DOM對象會存儲起來,當狀態或者其它數據改變,從新渲染組件(從新生成一套虛擬DOM對象)
- 把從新生成的虛擬DOM 和 以前存儲起來的虛擬 DOM進行對比 =>把不同的以補丁的形式存儲起來(存儲的仍是對象)
- 從新渲染的過程,只是把補丁渲染到頁面中,原有渲染過可是沒有改變的東西是不須要處理的