1.1 virtualDom是什么
virtualDom是基于dom的基礎(chǔ)上抽象了一個由多個js對象組成的一個數(shù)據(jù)結(jié)構(gòu)(樹).
1.2 重繪與回流
回流:節(jié)點的大小與位置發(fā)生變化,會導(dǎo)致回流
重繪:是指將渲染樹的每個節(jié)點的信息轉(zhuǎn)換為屏幕上的像素的過程叫重繪
所以,回流必然導(dǎo)致重繪,(不涉及位置和大小的改變,如相同字?jǐn)?shù)的文字的改變只觸發(fā)重繪) 重繪不一定會觸發(fā)回流,兩者具備先后次序.
1.3 優(yōu)化
因為將渲染樹的信息繪制到屏幕上是一個較為耗時的操作,頻繁的操作dom而導(dǎo)致頻繁的觸發(fā)回流與重繪會大大影響性能.從而給用戶帶來不好的體驗.可以通過手動的方式來優(yōu)化:如樣式集中改變,使用fixed和absoulte等方法.
1.4 virtualDom對性能的優(yōu)化
如果沒有 Virtual DOM浅蚪,簡單來想就是直接重置 innerHTML,重繪渲染樹,在一個大型列表所有數(shù)據(jù)都變了的情況下,重繪渲染樹其實是一個還算合理的操作,相比于virtualDom來說,此時的效率更高,性能更好(虛擬dom多了一步j(luò)s計算diff的過程),但是在只有一些只有一行數(shù)據(jù)改變等情況下,重繪渲染樹就會顯得有大量的浪費.
為什么要有 Virtual DOM: 1)不管你的數(shù)據(jù)變化多少萌京,它保證了每次重繪的性能都可以接受瓮下;2) 你依然可以用類似重繪整個dom樹 的思路去寫你的應(yīng)用僻造。
1.5 為什么要用框架
框架的意義在于為你掩蓋底層的 DOM 操作,從而讓你的代碼更容易維護,沒有任何框架可以比純手動的優(yōu)化 DOM 操作更快慧耍,因為框架的 DOM 操作層需要應(yīng)對任何上層 API 可能產(chǎn)生的操作趋翻,它的實現(xiàn)必須是普適的,針對任何一個優(yōu)化點,我門都可以寫出比框架更快的手動優(yōu)化,但是在構(gòu)建一個實際應(yīng)用的時候,出于可維護性的考慮脖咐,我們不會為每一個地方都去做手動優(yōu)化师崎∧眨框架可以給你的保證是,你在不需要手動優(yōu)化的情況下犁罩,依然可以給你提供過得去的性能齐蔽。
2.代碼實現(xiàn)一個React - Virtual Dom
2.1 DOM元素分析
DOM是很慢的。如果我們把一個簡單的div元素的屬性都打印出來昼汗,你會看到:<img src="https://pic2.zhimg.com/50/d5cda33e28d83ba12368202645f9e35b_hd.jpg" data-rawwidth="1239" data-rawheight="336" class="origin_image zh-lightbox-thumb" width="1239" data-original="https://pic2.zhimg.com/d5cda33e28d83ba12368202645f9e35b_r.jpg"/>而這僅僅是第一層肴熏。真正的 DOM 元素非常龐大鬼雀,這是因為標(biāo)準(zhǔn)就是這么設(shè)計的顷窒。而且操作它們的時候你要小心翼翼,輕微的觸碰可能就會導(dǎo)致頁面重排,這可是殺死性能的罪魁禍?zhǔn)仔O鄬τ?DOM 對象鸦做,原生的 JavaScript 對象處理起來更快,而且更簡單谓着。DOM 樹上的結(jié)構(gòu)泼诱、屬性信息我們都可以很容易地用
JavaScript 對象表示出來:var element = {
tagName: 'ul', // 節(jié)點標(biāo)簽名
props: { // DOM的屬性,用一個對象存儲鍵值對
id: 'list'
},
children: [ // 該節(jié)點的子節(jié)點
{tagName: 'li', props: {class: 'item'}, children: ["Item 1"]},
{tagName: 'li', props: {class: 'item'}, children: ["Item 2"]},
{tagName: 'li', props: {class: 'item'}, children: ["Item 3"]},
]
}
上面對應(yīng)的HTML寫法是:
<ul id='list'>
<li class='item'>Item 1</li>
<li class='item'>Item 2</li>
<li class='item'>Item 3</li>
</ul>
</ul>既然原來 DOM 樹的信息都可以用 JavaScript 對象來表示赊锚,反過來治筒,你就可以根據(jù)這個用 JavaScript 對象表示的樹結(jié)構(gòu)來構(gòu)建一棵真正的DOM樹。
2.2 用JS對象模擬DOM樹
用 JavaScript 來表示一個 DOM 節(jié)點是很簡單的事情舷蒲,你只需要記錄它的節(jié)點類型耸袜、屬性,還有子節(jié)點:
element.jsfunction Element (tagName, props, children) {
this.tagName = tagName
this.props = props
this.children = children
}
module.exports = function (tagName, props, children) {
return new Element(tagName, props, children)
}
例如上面的 DOM 結(jié)構(gòu)就可以簡單的表示:var el = require('./element')
var ul = el('ul', {id: 'list'}, [
el('li', {class: 'item'}, ['Item 1']),
el('li', {class: 'item'}, ['Item 2']),
el('li', {class: 'item'}, ['Item 3'])
])
現(xiàn)在ul只是一個 JavaScript 對象表示的 DOM 結(jié)構(gòu)牲平,頁面上并沒有這個結(jié)構(gòu)堤框。我們可以根據(jù)這個ul來構(gòu)建:
Element.prototype.render = function () {
var el = document.createElement(this.tagName) // 根據(jù)tagName構(gòu)建
var props = this.props
for (var propName in props) { // 設(shè)置節(jié)點的DOM屬性
var propValue = props[propName]
el.setAttribute(propName, propValue)
}
var children = this.children || []
children.forEach(function (child) {
var childEl = (child instanceof Element)
? child.render() // 如果子節(jié)點也是虛擬DOM,遞歸構(gòu)建DOM節(jié)點
: document.createTextNode(child) // 如果字符串纵柿,只構(gòu)建文本節(jié)點
el.appendChild(childEl)
})
return el
}
render方法會根據(jù)tagName構(gòu)建一個真正的DOM節(jié)點蜈抓,然后設(shè)置這個節(jié)點的屬性,最后遞歸地把自己的子節(jié)點也構(gòu)建起來昂儒。所以只需要:
var ulRoot = ul.render()
document.body.appendChild(ulRoot)
上面的ulRoot是真正的DOM節(jié)點沟使,把它塞入文檔中,這樣body里面就有了真正的<ul>的DOM結(jié)構(gòu):<ul id='list'>
<li class='item'>Item 1</li>
<li class='item'>Item 2</li>
<li class='item'>Item 3</li>
</ul>