React 虛擬DOM

virtul DOM 也就是虛擬節(jié)點嘱丢。通過JS的Object對象模擬DOM中的真實節(jié)點對象节视,再通過特定的render方法將其渲染成真實的DOM節(jié)點双戳。

一、渲染步驟

生成vNode---->渲染成真實節(jié)點 --------->掛載到頁面--------->diff比較
1蹋凝、模擬方法和渲染方法
需求,生成一個如下圖所示的DOM結(jié)構(gòu)


image.png

調(diào)用

let virtualDom1 = createElement('ul', {class: 'list'}, [
    createElement('li', {class: 'item'}, ['a']),
    createElement('li', {class: 'item'}, ['b']),
    createElement('li', {class: 'item'}, ['c']),
])
let virtualDom2 = createElement('ul', {class: 'list'}, [
    createElement('li', {class: 'item'}, ['1']),
    createElement('li', {class: 'item'}, ['2']),
    createElement('li', {class: 'item'}, ['3']),
])
let el = render(virtualDom);
renderDom(el, window.root);
let patchs = diff(virtualDom1, virtualDom2);

生成虛擬對象的方法createElement

function createElement(type, props, children) {
    return new Element(type, props, children)
}
class Element{
    constructor(type, props, children){
        this.type = type;
        this.props = props;
        this.children = children
    }
}

將虛擬對象渲染成真實DOM的render方法

//render方法將vNode轉(zhuǎn)化成真實DOM
function render(eleObj){
    //創(chuàng)建元素
    let el = document.createElement(eleObj.type);
    //設(shè)置屬性
    for(let key in eleObj.props) {
        setAttr(el, key, eleObj.props[key]);
    }
    //遞歸渲染子元素
    eleObj.children.foEach(child => {
        child = child instanceof Element ? render(child) : document.createTextNode(child);
        el.appendChild(child);
    })
}
setAttr(node, key, value) {
    switch(key) {
        case 'value':
            if (node.tagName.toUpperCase() === 'INPUT' || node.tagName.toUpperCase() === 'TEXTAREA') {
                node.value = value;          
            }else {
                node.setAttribute(key, value);
            }
            break;
        case 'style':
            node.style.cssText = value;
            break;
        default:
            node.setAttribute(key, value);
            break;
    }
}

渲染節(jié)點到頁面的方法renderDom

//將真實DOM渲染到頁面
function renderDom(el, target) {
    target.appendChild(el);
}

二总棵、DOM DIFF 算法

1鳍寂、核心思想

DOM DIFF 就是比較兩個虛擬DOM的區(qū)別,實際上就是比較兩個對象的區(qū)別情龄。根據(jù)兩個虛擬對象創(chuàng)建出補丁迄汛,描述改變的內(nèi)容。將這個補丁用來更新DOM骤视。


image.png

【注意】不會更改所有節(jié)點鞍爱,只更改有改變的部分

2、DOM DIFF 兩種優(yōu)化策略

1)分層比較尚胞,一層一層比硬霍,不會跨級對比
2)如果一層的對象只是換了下位置,可以通過key值直接換位置笼裳。

2唯卖、算法實現(xiàn)

差異計算:先序深度優(yōu)先遍歷


image.png

規(guī)則:
1粱玲、若節(jié)點類型不相同,直接采用替換模式拜轨,{type:'REPLACE',newNode:newNode}
2抽减、當(dāng)節(jié)點類型相同時,去看一下屬性是否相同橄碾,產(chǎn)生一個屬性的補丁包卵沉,比如{type:'ATTRS',attrs:{class: 'list-group'}
3、新的DOM節(jié)點不存在法牲,也返回一個不存在的補丁包{type:'REMOVE',index:XXX}
4史汗、文本的變化{type:'TEXT', text:1}

DIff 算法
//diff 算法
let Index = 0;
function diff(oldTree, newTree) {
    let patches = {};
    let index = 0;
    //遞歸數(shù)比較后的結(jié)果放到補丁包中
    walk(oldTree, newTree, index, patches);
    return patches;
}
function walk(oldTree, newTree, index, patches){
    let currentPatch = [];//每個元素都有一個補丁對象
    if (!newTree) {
        currentPatch.push({type:'REMOVE', index})
    } 
    if (isString(oldTree) && isString(newTree)) {
        // 判斷文本是否一致
        if (oldTree !== newTree) {
            currentPatch.push({type:'TEXT',text:newTree}); 
        }
    }else if(oldTree.type === newTree.type) {
        //比較屬性是否有更改
        let attrs = diffAttr(oldTree.props, newTree.props);
        if(Object.keys(attrs).length) {
            currentPatch.push({type:'ATTRS', attrs});
        }
        // 如果有兒子節(jié)點,遍歷子節(jié)點
          diffChildren(oldTree.children, newTree.children, index, patches);
    } else {
        // 節(jié)點類型不同的時候拒垃,直接替換
        currentPatch.push({type:'REPLACE', newTree});
    }
    // 當(dāng)前元素有補丁的情況下停撞,將元素和補丁對應(yīng)起來,放到大補丁包中
    if(currentPatch.length) {
        patches[index] = currentPatch; 
    }
}
function diffAttr(oldAttrs, newAttrs) {
    let patch = {};
    for(let key in oldAttrs) {
        if(oldAttrs[key] !== newAttrs[key]) {
            patch[key] = newAttrs[key];//有可能是undefined悼瓮,新節(jié)點沒有舊節(jié)點的屬性      
        }
    }
    for(let key in newAttrs) {
        //老節(jié)點沒有新節(jié)點的屬性
        if(! oldAttrs.hasOwnProperty(key)) {
            patch[key] = newAttrs[key]
        }
    }
    return patch;
}

function diffChildren(oldChildren, newChildren, index, patches){
    // 比較老的第一個和新的第一個
    oldChildren.forEach((child, idx) => {
        // 記得索引得改
        // Index 每次傳遞給walk時戈毒,index是遞增的,所有節(jié)點都基于一個序號實現(xiàn)横堡,因此需要維護一個全局Index
        walk(child, newChildren[idx], ++Index, patches);
    }) 
}


function isString(node) {
    return Object.prototype.toString.call(node) === '[object string]';
}


function patch(node, patches) {
 // 給某個元素打補丁
 
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末埋市,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子命贴,更是在濱河造成了極大的恐慌道宅,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件套么,死亡現(xiàn)場離奇詭異培己,居然都是意外死亡,警方通過查閱死者的電腦和手機胚泌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進店門省咨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人玷室,你說我怎么就攤上這事零蓉。” “怎么了穷缤?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵敌蜂,是天一觀的道長。 經(jīng)常有香客問我津肛,道長章喉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮秸脱,結(jié)果婚禮上落包,老公的妹妹穿的比我還像新娘。我一直安慰自己摊唇,他們只是感情好咐蝇,可當(dāng)我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著巷查,像睡著了一般有序。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上岛请,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天旭寿,我揣著相機與錄音,去河邊找鬼崇败。 笑死许师,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的僚匆。 我是一名探鬼主播,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼搭幻,長吁一口氣:“原來是場噩夢啊……” “哼咧擂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起檀蹋,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤松申,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后俯逾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贸桶,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年桌肴,在試婚紗的時候發(fā)現(xiàn)自己被綠了皇筛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡坠七,死狀恐怖水醋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情彪置,我是刑警寧澤拄踪,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站拳魁,受9級特大地震影響惶桐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一姚糊、第九天 我趴在偏房一處隱蔽的房頂上張望贿衍。 院中可真熱鬧,春花似錦叛拷、人聲如沸舌厨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽裙椭。三九已至,卻和暖如春署浩,著一層夾襖步出監(jiān)牢的瞬間揉燃,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工筋栋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留炊汤,地道東北人。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓弊攘,卻偏偏與公主長得像抢腐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子襟交,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,851評論 2 361

推薦閱讀更多精彩內(nèi)容