面试为什么要面jvm 算法(面试官问如何理解Virtual)
面试为什么要面jvm 算法(面试官问如何理解Virtual)因此我们采用JS对象模拟的方法,将DOM的比对操作放在JS层,减少浏览器不必要的重绘,提高效率。<!DOCTYPE html> <html lang="en"> <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-c
一、vdom是什么?
vdom是虚拟DOM(Virtual DOM)的简称,指的是用JS模拟的DOM结构,将DOM变化的对比放在JS层来做。换而言之,vdom就是JS对象。
如下DOM结构:
<ul id="list"> <li class="item">Item1</li> <li class="item">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 lang="en"> <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://img.aigexing.comhttps://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算法的比对过程。所以,上述性能优势仅仅适用于大量数据的渲染并且改变的数据只是一小部分的情况。
你会发现,只有改变的栏目才闪烁,也就是进行重绘,数据没有改变的栏目还是保持原样,这样就大大节省了浏览器重新渲染的开销。四、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高级面试
前端进阶与面试指南