1. 程式人生 > 其它 >虛擬節點與DOM Diff演算法

虛擬節點與DOM Diff演算法

 DOM Diff

1.對比兩個虛擬節點,找出差異,再對應到真實DOM ,進行補丁;

由於DOM操作損耗效能,所以應求得最小代價

2.遵循結構上一一對應關係,索引值

 

 

 

 

 

 3.p span 交換

 

 

 4.Diff——深度優先遍歷

實現

createElement()方法 將虛擬節點變為物件

function createElement(type,props,children){
    return new Element(type,props,children);
}

 

 

 

 虛擬節點變為真實節點 渲染為DOM結構

render()

/vDOM到rDOM
function
render (vDom){ const {type,props,children}=vDom, el = document.createElement(type); for( let key in props){ setAttrs( el,key, props[key]); } //console.log(el);

 

 

 

 

 

 

 

 

 等同於

 

 

 

 

開始渲染DOM

renderDOM()

function renderDOM(rDom,rootEl){
     rootEl.appendChild(rDom);

 }

 

domDiff.js

import {
    ATTR,
    TEXT,
    REPLACE,
    REMOVE
}from '.patchTypes';
//從當前解構

//宣告
let patches ={},
    vnIndex = 0;


function domDiff(oldVDom,newVDom){
    let index = 0;
    vNodeWalk(oldVDom,newVDom,index);
    //虛擬節點遍歷
    return patches;

}

function vNodeWalk(oldNode,newNode,index){
    let vnPatch 
= []; if(!newNode){ vnPatch.push({ type:REMOVE, index }) }else if(typeof oldNode === 'string' && typeof newNode === 'string'){ if(oldNode !== newNode){ vnPatch.push({ type:TEXT, text:newNode }) } } else if(oldNode.type === newNode.type){ const attrPatch = attrsWalk(oldNode.props,newNode.props); console.log(Object.keys(attrPatch));//一層 class if(Object.keys(attrPatch).length>0){ vnPatch.push({ type:ATTR, attrs:attrPatch }); } //遍歷子 childrenWalk(oldNode.children,newNode.children); } else{ vnPatch.push ({ type:REPLACE, newNode }); } //判斷是否有patch if(vnPatch.length>0){ patches[index] = vnPatch; } } //對比props function attrsWalk( oldAttrs,newAttrs){ let attrPatch={}; //修改屬性 for (let key in oldAttrs){ if(oldAttrs[key]!== newAttrs){ attrPatch[key] = newAttrs[key]; //打補丁 } } //是否有屬性 for (let key in newAttrs){ //遍歷新,對比舊判斷是否有 if(!oldAttrs.hasOwnProperty(key)){ attrPatch[key] = newAttrs[key]; } } return attrPatch; } function childrenWalk(oldChildren, newChildren){ oldChildren.map((c,idx) => { vNodeWalk(c,newChildren[idx], ++vnIndex) }) } export default domDiff;

 

 

 

 實現打補丁

import {
  ATTR,
  TEXT,
  REPLACE,
  REMOVE
} from './js/patchTypes';
import { setAttrs, render } from './js/virtualDom';
import Element from './js/Element';

let finalPatches = {},
    rnIndex = 0;

function doPatch (rDom, patches) {
  finalPatches = patches;
  rNodeWalk(rDom);
}

function rNodeWalk (rNode) {
  const rnPatch = finalPatches[rnIndex ++],
        childNodes = rNode.childNodes;
  
  [...childNodes].map((c) => {
    rNodeWalk(c);
  });

  if (rnPatch) {
    patchAction(rNode, rnPatch);
  }
}

function patchAction (rNode, rnPatch) {
  rnPatch.map((p) => {
    switch (p.type) {
      case ATTR:
        for (let key in p.attrs) {
          const value = p.attrs[key];

          if (value) {
            setAttrs(rNode, key, value);
          } else {
            rNode.removeAttribute(key);
          }
        }
        break;
      case TEXT:
        rNode.textContent = p.text;
        break;
      case REPLACE:
        const newNode = (p.newNode instanceof Element)
                      ? render(p.newNode)
                      : document.createTextNode(p.newNode);
        
        rNode.parentNode.replaceChild(newNode, rNode);
        break;
      case REMOVE:
        rNode.parentNode.removeChild(rNode);
        break;
      default:
        break;
    }
  });
}

export default doPatch;

// vNode = virtual Node
// vnPatch = virtual Node patch
// rNode = real Node
// rnPatch = real Node patch