溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

面試官問:如何理解Virtual DOM?

發布時間:2020-07-08 04:19:37 來源:網絡 閱讀:397 作者:可樂程序員 欄目:web開發

一、vdom是什么?

vdom是虛擬DOM(Virtual DOM)的簡稱,指的是用JS模擬的DOM結構,將DOM變化的對比放在JS層來做。換而言之,vdom就是JS對象。

如下DOM結構:

<ul?id="list">
?<li>Item1</li>
?<li>Item2</li>
</ul>
復制代碼

映射成虛擬DOM就是這樣:

{
?tag:?"ul",
?attrs:?{
?id:?"list"
?},
?children:?[
?{
?tag:?"li",
?attrs:?{?className:?"item"?},
?children:?["Item1"]
?},?{
?tag:?"li",
?attrs:?{?className:?"item"?},
?children:?["Item2"]
?}
?]
}?
復制代碼

二、為什么要用vdom?

現在有一個場景,實現以下需求:

[
?{
?name:?"張三",
?age:?"20",
?address:?"北京"
?},
?{
?name:?"李四",
?age:?"21",
?address:?"武漢"
?},
?{
?name:?"王五",
?age:?"22",
?address:?"杭州"
?},
]
復制代碼

將該數據展示成一個表格,并且隨便修改一個信息,表格也跟著修改。 用jQuery實現如下:

<!DOCTYPE?html>
<html>
<head>
?<meta?charset="UTF-8">
?<meta?name="viewport"?content="width=device-width,?initial-scale=1.0">
?<meta?http-equiv="X-UA-Compatible"?content="ie=edge">
?<title>Document</title>
</head>
<body>
?<div?id="container"></div>
?<button?id="btn-change">改變</button>
?<script?src="https://cdn.bootcss.com/jquery/3.2.0/jquery.js"></script>
?<script>
?const?data?=?[{
?name:?"張三",
?age:?"20",
?address:?"北京"
?},
?{
?name:?"李四",
?age:?"21",
?address:?"武漢"
?},
?{
?name:?"王五",
?age:?"22",
?address:?"杭州"
?},
?];
?//渲染函數
?function?render(data)?{
?const?$container?=?$('#container');
?$container.html('');
?const?$table?=?$('<table>');
?//?重繪一次
?$table.append($('<tr><td>name</td><td>age</td><td>address</td></tr>'));
?data.forEach(item?=>?{
?//每次進入都重繪
?$table.append($(`<tr><td>${item.name}</td><td>${item.age}</td><td>${item.address}</td></tr>`))
?})
?$container.append($table);
?}
?
?$('#btn-change').click(function?()?{
?data[1].age?=?30;
?data[2].address?=?'深圳';
?render(data);
?});
?</script>
</body>
</html>
復制代碼

這樣點擊按鈕,會有相應的視圖變化,但是你審查以下元素,每次改動之后,table標簽都得重新創建,也就是說table下面的每一個欄目,不管是數據是否和原來一樣,都得重新渲染,這并不是理想中的情況,當其中的一欄數據和原來一樣,我們希望這一欄不要重新渲染,因為DOM重繪相當消耗瀏覽器性能。

因此我們采用JS對象模擬的方法,將DOM的比對操作放在JS層,減少瀏覽器不必要的重繪,提高效率。

當然有人說虛擬DOM并不比真實的DOM快,其實也是有道理的。當上述table中的每一條數據都改變時,顯然真實的DOM操作更快,因為虛擬DOM還存在js中diff算法的比對過程。所以,上述性能優勢僅僅適用于大量數據的渲染并且改變的數據只是一小部分的情況。

虛擬DOM更加優秀的地方在于:

1、它打開了函數式的UI編程的大門,即UI = f(data)這種構建UI的方式。

2、可以將JS對象渲染到瀏覽器DOM以外的環境中,也就是支持了跨平臺開發,比如ReactNative。

另外大家可以參考尤大的一些回答: www.zhihu.com/question/31…

三、使用snabbdom實現vdom

snabbdom地址:github.com/snabbdom/sn…

這是一個簡易的實現vdom功能的庫,相比vue、react,對于vdom這塊更加簡易,適合我們學習vdom。vdom里面有兩個核心的api,一個是h函數,一個是patch函數,前者用來生成vdom對象,后者的功能在于做虛擬dom的比對和將vdom掛載到真實DOM上。

簡單介紹一下這兩個函數的用法:

  • h('標簽名', {屬性}, [子元素])

  • h('標簽名', {屬性}, [文本])

  • patch(container, vnode) container為容器DOM元素

  • patch(vnode, newVnode)

現在我們就來用snabbdom重寫一下剛才的例子:

<!DOCTYPE?html>
<html>
<head>
?<meta?charset="UTF-8">
?<meta?name="viewport"?content="width=device-width,?initial-scale=1.0">
?<meta?http-equiv="X-UA-Compatible"?content="ie=edge">
?<title>Document</title>
</head>
<body>
?<div?id="container"></div>
?<button?id="btn-change">改變</button>
?<script?src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom.js"></script>
?<script?src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-class.js"></script>
?<script?src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-props.js"></script>
?<script?src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-style.js"></script>
?<script?src="https://cdn.bootcss.com/snabbdom/0.7.3/snabbdom-eventlisteners.min.js"></script>
?<script?src="https://cdn.bootcss.com/snabbdom/0.7.3/h.js"></script>
?<script>
?let?snabbdom?=?window.snabbdom;
?//?定義patch
?let?patch?=?snabbdom.init([
?snabbdom_class,
?snabbdom_props,
?snabbdom_style,
?snabbdom_eventlisteners
?]);
?//定義h
?let?h?=?snabbdom.h;
?const?data?=?[{
?name:?"張三",
?age:?"20",
?address:?"北京"
?},
?{
?name:?"李四",
?age:?"21",
?address:?"武漢"
?},
?{
?name:?"王五",
?age:?"22",
?address:?"杭州"
?},
?];
?data.unshift({name:?"姓名",?age:?"年齡",?address:?"地址"});
?let?container?=?document.getElementById('container');
?let?vnode;
?const?render?=?(data)?=>?{
?let?newVnode?=?h('table',?{},?data.map(item?=>?{?
?let?tds?=?[];
?for(let?i?in?item)?{
?if(item.hasOwnProperty(i))?{
?tds.push(h('td',?{},?item[i]?+?''));
?}
?}
?return?h('tr',?{},?tds);
?}));
?if(vnode)?{
?patch(vnode,?newVnode);
?}?else?{
?patch(container,?newVnode);
?}
?vnode?=?newVnode;
?}
?render(data);
?let?btnChnage?=?document.getElementById('btn-change');
?btnChnage.addEventListener('click',?function()?{
?data[1].age?=?30;
?data[2].address?=?"深圳";
?//re-render
?render(data);
?})
?</script>
</body>
</html>
復制代碼

再進入頁面:

面試官問:如何理解Virtual DOM?


你會發現,只有改變的欄目才閃爍,也就是進行重繪,數據沒有改變的欄目還是保持原樣,這樣就大大節省了瀏覽器重新渲染的開銷。四、diff算法

1、什么是diff算法?

所謂diff算法,就是用來找出兩段文本之間的差異的一種算法。

作為一個前端,大家經常會聽到diff算法這個詞,其實diff并不是前端原創的算法,其實這一個算法早已在linux的diff命令中有所體現,并且大家常用的git diff也是運用的diff算法。

2、vdom為什么用diff算法

DOM操作是非常昂貴的,因此我們需要盡量地減少DOM操作。這就需要找出本次DOM必須更新的節點來更新,其他的不更新,這個找出的過程,就需要應用diff算法。

3、vdom中diff算法的簡易實現

以下代碼只是幫助大家理解diff算法的原理和流程,不可用于生產環境。

將vdom轉化為真實dom:

const?createElement?=?(vnode)?=>?{
?let?tag?=?vnode.tag;
?let?attrs?=?vnode.attrs?||?{};
?let?children?=?vnode.children?||?[];
?if(!tag)?{
?return?null;
?}
?//創建元素
?let?elem?=?document.createElement(tag);
?//屬性
?let?attrName;
?for?(attrName?in?attrs)?{
?if(attrs.hasOwnProperty(attrName))?{
?elem.setAttribute(attrName,?attrs[attrName]);
?}
?}
?//子元素
?children.forEach(childVnode?=>?{
?//給elem添加子元素
?elem.appendChild(createElement(childVnode));
?})
?//返回真實的dom元素
?return?elem;
}
復制代碼

用簡易diff算法做更新操作:

function?updateChildren(vnode,?newVnode)?{
?let?children?=?vnode.children?||?[];
?let?newChildren?=?newVnode.children?||?[];
?children.forEach((childVnode,?index)?=>?{
?let?newChildVNode?=?newChildren[index];
?if(childVnode.tag?===?newChildVNode.tag)?{
?//深層次對比,?遞歸過程
?updateChildren(childVnode,?newChildVNode);
?}?else?{
?//替換
?replaceNode(childVnode,?newChildVNode);
?}
?})
}
復制代碼

參考資料:

知乎尤雨溪回答

揭秘一線互聯網企業 前端JavaScript高級面試

前端進階與面試指南

面試官問:如何理解Virtual DOM?


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女