文章首發於個人博客 https://github.com/mcuking/bl...相關代碼請查閱 https://github.com/mcuking/bl...node
上一講中 state 發生變化時,將會從新渲染,使用新生成的 dom 替換老的 dom。可是當 dom 樹很大時,而每次更改的地方很小時,可能只是修改某個節點的屬性,這種粗暴的替換方式就顯得很浪費性能。react
所以 react 提出了 diff 算法:vnode(純 js 對象) 表明 dom, 在渲染以前,先比較出 oldvnode 和 newvode 的區別。而後增量的更新 dom。git
如何增量更新呢?github
在第一講中,render 函數裏對於每個斷定爲 dom 類型的 VDOM,都是直接建立一個新的 DOM:算法
... else if(typeof vnode.nodeName == "string") { dom = document.createElement(vnode.nodeName) ... } ...
必定要建立一個 新的 DOM 結構嗎?瀏覽器
考慮以下狀況:
如一個組件, 初次渲染爲 renderBefore, 調用 setState 再次渲染爲 renderAfter 調用 setState 再再次渲染爲 renderAfterAfter。 VNODE 以下:dom
const renderBefore = { tagName: 'div', props: { width: '20px', className: 'xx' }, children:[vnode1, vnode2, vnode3] } const renderAfter = { tagName: 'div', props: { width: '30px', title: 'yy' }, children:[vnode1, vnode2] } const renderAfterAfter = { tagName: 'span', props: { className: 'xx' }, children:[vnode1, vnode2, vnode3] }
renderBefore 和 renderAfter 都是 div,只不過 props 和 children 有部分區別,那咱們是否是能夠經過修改 DOM 屬性,修改 DOM 子節點,把 rederBefore 變化爲 renderAfter 呢?函數
而 renderAfter 和 renderAfterAfter 屬於不一樣的 DOM 類型, 瀏覽器還沒提供修改 DOM 類型的 Api,是沒法複用的,所以必定要建立新的 DOM 的。性能
因此 diff 機制以下:spa
相同元素:
所以代碼大體以下:
... else if(typeof vnode.nodeName == "string") { if(!olddom || olddom.nodeName != vnode.nodeName.toUpperCase()) { createNewDom(vnode, parent, comp, olddom) } else { diffDOM(vnode, parent, comp, olddom) // 包括 更新屬性, 子節點複用 } } ...
在後面的兩講中我將分別介紹更新屬性以及複用子節點這兩種機制。
相關文章