新聞中心
前言

創(chuàng)新互聯(lián)是專(zhuān)業(yè)的化州網(wǎng)站建設(shè)公司,化州接單;提供網(wǎng)站設(shè)計(jì)、成都網(wǎng)站設(shè)計(jì),網(wǎng)頁(yè)設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專(zhuān)業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行化州網(wǎng)站開(kāi)發(fā)網(wǎng)頁(yè)制作和功能擴(kuò)展;專(zhuān)業(yè)做搜索引擎喜愛(ài)的網(wǎng)站,專(zhuān)業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來(lái)合作!
Vue和React的Render函數(shù)中都涉及到了Virtual DOM的概念,Virtual DOM也是性能優(yōu)化上的重要一環(huán),同時(shí)突破了直接操作真實(shí)DOM的瓶頸,本文帶著以下幾個(gè)問(wèn)題來(lái)闡述Virtual DOM。
1.為什么要操作虛擬 DOM?
2.什么是虛擬 DOM?
3.手把手教你實(shí)現(xiàn)虛擬 DOM 渲染真實(shí) DOM
希望閱讀本文之后,能夠讓你深入的了解虛擬 DOM并且在開(kāi)發(fā)和面試中收益。
為什么要操作虛擬 DOM
為了幫助我們更好的理解為什么要操作虛擬 DOM,我們先從瀏覽器渲染[1]一個(gè) HTML 文件需要做哪些事情說(shuō)起:
瀏覽器渲染機(jī)制大致可以分為以下 5 步走:
1.創(chuàng)建 DOM tree
2.創(chuàng)建 Style Rules
3.構(gòu)建 Render tree
4.布局 Layout
5.繪制 Painting
我們過(guò)去使用原生JavaScript和jquery去操作真實(shí)DOM的時(shí)候,瀏覽器會(huì)從構(gòu)建 DOM 開(kāi)始從頭到尾的執(zhí)行一遍渲染的流程。
在一次開(kāi)發(fā)中,假如產(chǎn)品告訴你一個(gè)需求,你需要在一次操作中更新10個(gè)DOM節(jié)點(diǎn),理想狀態(tài)是瀏覽器一次性構(gòu)建完DOM樹(shù),再執(zhí)行后續(xù)操作。但瀏覽器沒(méi)這么智能,收到第一個(gè)更新 DOM 請(qǐng)求后,并不知道后續(xù)還有9次更新操作,因此會(huì)馬上執(zhí)行流程,最終執(zhí)行 10 次流程。
過(guò)了一會(huì)產(chǎn)品經(jīng)理把你叫過(guò)去和你說(shuō)把需求改一下,此時(shí)你又需要操作一次 DOM 的更新,那么這個(gè)時(shí)候之前做的 10 次 DOM 操作就是白白浪費(fèi)性能,浪費(fèi)感情。
即使計(jì)算機(jī)硬件一直在更新迭代,但是操作DOM的代價(jià)仍舊是昂貴的,頻繁操作 DOM 還是會(huì)出現(xiàn)頁(yè)面卡頓,影響用戶的體驗(yàn)。真實(shí)的 DOM 節(jié)點(diǎn),哪怕一個(gè)最簡(jiǎn)單的 div 也包含著很多屬性,可以打印出來(lái)直觀感受一下:
如此多的屬性,如果每次對(duì) DOM 結(jié)構(gòu)都進(jìn)行更新,一次,兩次,三次...一百次....一千次...,可想而知,是多么龐大的數(shù)據(jù)量。
因此虛擬DOM就是為了解決這個(gè)瀏覽器性能問(wèn)題而被設(shè)計(jì)出來(lái)的。例如前面的例子,假如一次操作中有 10 次更新 DOM 的動(dòng)作,虛擬DOM不會(huì)立即操作DOM,而是將這 10 次更新 DOM 的動(dòng)作通過(guò)Diff算法最終生成一個(gè)js對(duì)象,然后通知瀏覽器去執(zhí)行一次繪制工作,這樣可以避免大量的無(wú)謂的計(jì)算量。
什么是虛擬 DOM
虛擬 DOM[2]就是我們上面所說(shuō)的js對(duì)象。
其本質(zhì)上就是在JS和DOM之間做了一個(gè)緩存??梢灶?lèi)比 CPU 和硬盤(pán),既然硬盤(pán)這么慢,我們就在它們之間加個(gè)緩存:既然 DOM 這么慢,我們就在它們 JS 和 DOM 之間加個(gè)緩存。CPU(JS)只操作內(nèi)存(Virtual DOM),最后的時(shí)候再把變更寫(xiě)入硬盤(pán)(DOM),直接操作內(nèi)存中的 JS 對(duì)象的速度顯然要更快。
- function vnode(tag, data, key, children, text) {
- return {
- tag,
- data,
- key,
- children,
- text
- }
- }
舉個(gè)栗子:
假如我們有這樣的一個(gè) DOM 樹(shù)
- 前端簡(jiǎn)報(bào)
- vue
那么,我們?cè)趺从?js 的對(duì)象來(lái)對(duì)應(yīng)到這個(gè)樹(shù)呢?
- {
- tag: 'ul', // 元素標(biāo)簽
- data: { // 屬性
- class: 'list'
- },
- key: '',
- text: '', // 文本內(nèi)容
- children: [
- {
- tag: "li",
- data: {
- class: "item"
- },
- key: '',
- text: '',
- children: [
- {
- tag: undefined,
- data: undefined,
- key: undefined,
- text: '前端簡(jiǎn)報(bào)',
- children: []
- }
- ]
- },
- {
- tag: "li",
- data: "",
- key: '',
- text: '',
- children: [
- {
- tag: undefined,
- data: undefined,
- key: undefined,
- text: 'vue',
- children: []
- }
- ]
- }
- ] // 子元素
- }
由此可知:DOM tree的信息都可以用JavaScript對(duì)象來(lái)表示,反過(guò)來(lái),我們也可以用 JavaScript對(duì)象表示的樹(shù)結(jié)構(gòu)來(lái)構(gòu)建一棵真正的DOM樹(shù)。
實(shí)現(xiàn)虛擬 DOM 渲染真實(shí) DOM
有了JavaScript對(duì)象之后如何轉(zhuǎn)化為真實(shí)的 DOM 樹(shù)結(jié)構(gòu)呢?
ul 和 li 在 js 對(duì)象中,頁(yè)面上并沒(méi)有此結(jié)構(gòu),所以我們需要把ul和li轉(zhuǎn)化為和
標(biāo)簽
而文本標(biāo)簽我們定義 Vnode 為:
- {
- tag: undefined,
- data: undefined,
- key: undefined,
- text: 'vue',
- children: []
- }
故可以判斷tag的類(lèi)型來(lái)確定創(chuàng)建元素的類(lèi)型.
- function createElm(vnode) {
- let { tag, data, children, key, text } = vnode;
- if (typeof tag == "string") {
- vnode.el = document.createElement(tag); //創(chuàng)建元素放到vnode.el上
- children.forEach(child => {
- vnode.el.appendChild(createElm(child))
- })
- } else {
- vnode.el = document.createTextNode(text); //創(chuàng)建文本
- }
- return vnode.el
- }
如果子節(jié)點(diǎn)存在并且也是虛擬DOM的話,我們通過(guò)遞歸調(diào)用創(chuàng)建子節(jié)點(diǎn)。
創(chuàng)建 DOM 樹(shù)結(jié)構(gòu)之后我們需要設(shè)置節(jié)點(diǎn)的屬性,即處理虛擬 DOM 中的data屬性。
- function updateProperties(vnode) {
- let el = vnode.el;
- let newProps = vnode.data || {};
- for (let key in newProps) {
- if (key == "style") {
- for (let styleName in newProps.style) {
- el.style[styleName] = newProps.style[styleName];
- }
- } else if (key == "class") {
- el.className = newProps.class;
- } else {
- el.setAttribute(key, newProps[key]);
- }
- }
- }
在我們創(chuàng)建元素標(biāo)簽之后調(diào)用updateProperties方法即可
把上面創(chuàng)建出來(lái)的真實(shí) DOM 結(jié)構(gòu) vnode.el 添加到文檔當(dāng)中即可呈現(xiàn)出我們需要的真實(shí) DOM 結(jié)構(gòu)
- let parentElm = document.getElementById("app").parentNode; 獲取之前app的父親body
- parentElm.insertBefore(createElm(vnode), document.getElementById("app").nextSibling); //body里在老的app后面插入真實(shí)dom
- parentElm.removeChild(document.getElementById("app")); //刪除老的節(jié)點(diǎn)
總結(jié)
以上就是本文的全部?jī)?nèi)容,我想我們現(xiàn)在應(yīng)該了解什么是虛擬DOM的概念了以及虛擬DOM是如何實(shí)現(xiàn)真實(shí)DOM渲染的。其中用到了主要用到了子節(jié)點(diǎn)的遞歸,下篇文章將講解虛擬節(jié)點(diǎn)的 diff 算法,敬請(qǐng)期待。
參考資料
[1]虛擬DOM介紹: https://www.jianshu.com/p/616999666920
[2]如何實(shí)現(xiàn)一個(gè) Virtual DOM 算法: 'https://github.com/livoras/blog/issues/13'
當(dāng)前名稱:虛擬DOM如何進(jìn)化為真實(shí)DOM
網(wǎng)站路徑:http://www.fisionsoft.com.cn/article/cojhcjp.html


咨詢
建站咨詢
