2018- 深入理解虚拟DOM ,它真的不快
深入理解虚拟DOM ,它真的不快
使用过
前端应用状态管理
在日益复杂的前端应用中,状态管理是一个经常被提及的话题,从早期的刀耕火种时代到
但是,双向数据绑定也并不是唯一的办法,还有一个非常粗暴有效的方式:一旦数据发生变化,重新绘制整个视图,也就是重新设置一下
说到这里,你会说这跟
什么是Virtual DOM
一个对象指的是
两个前提分别是
三个步骤指的是
Virtual DOM 三板斧
下面就将介绍
- 生成
Virtual DOM 树

- 对比两棵树的差异
比较两棵

比较“树”的差异,首先是要对树进行遍历,常用的有两种遍历算法,分别是深度优先遍历和广度优先遍历,一般的

例如,上面的
在这个差异对象中记录了有改变的节点,每一个发生改变的内容也不尽相同,但也是有迹可循,常见的差异包括四种,分别是:
- 替换节点
- 增加
/ 删除子节点 - 修改节点属性
- 改变文本内容
所以在记录差异的时候要根据不同的差异类型,记录不同的内容。
更新视图
在第二步得到整棵树的差异之后,就可以根据这些差异的不同类型,对
replaceChild()
appendChild()/removeChild()
setAttribute()/removeAttribute()
textContent

让我们来看看手动执行
在谈论
JS 部分:定义JavaScript 世界中的变化DOM 部分:使用DOM API 函数和属性执行更改
性能是根据整个过程的速度来衡量的,但了解每部分的速度也很重要,以便了解要优化的内容。
有两种方法可以创建和更新
① 字符串方式创建
使用字符串既快速又简单,但在更新方面并不是非常精细。对于字符串,
const userList = document.getElementById("user-list");
// JS 部分
const html = users
.map(function (user) {
return `
<div id="${user.id}" class=”user”>
<h2 class="header">${user.firstName} ${user.lastName}</h2>
<p class="email"><a href=”mailto:${user.email}”>EMAIL</a></p>
<p class="avg-grade">Average grade: ${user.avgGrade}</p>
<p class="enrolled">Enrolled: ${user.enrolled}</p>
</div>
`;
})
.join("");
// DOM 部分
userList.innerHTML = html;
我提到使用这种方法时存在局限性。请考虑以下示例:
const search = document.getElementById("search");
search.innerHTML = `<input class="search" type="text" value="foo">`;
// Change value to "bar"?
search.innerHTML = `<input class="search" type="text" value="bar">`;
虽然看起来上面的内容很简单,但它实际上并不起作用。当我们运行上面的代码时,原始<input>
元素被替换而不是更新,例如,如果用户有焦点的字段,他们将失去焦点。
② 使用
创建和更新
让我们使用这个方法重写用户列表示例:
const userList = document.getElementById(“user-list”);
// JS part
const = document.createDocumentFragment();
users.forEach(function(user){
const div = document.createElement(“div”);
div.id = user.id;
div.className =“user”;
const header = document.createElement(“h2”);
h2 .className =“header”;
h2.appendChild(
document.createTextNode(`$ {user.firstName} $ {user.lastName}`)
);
// ....
frag.appendChild(div);
});
// DOM部分
userList.innerHTML =“”;
userList.appendChild(FRAG);
这看起来不太好,但它仍然是创建
const search = document.getElementById(“search”);
search.innerHTML =`<input class ="search" type ="text"value ="foo">`;
//将值更改为“bar”?
search.querySelector("input")。value ="bar";
这次我们结合快速方便的字符串<input>
现在没有被替换,所以它不会像第一个例子那样引起
动手实现Virtual DOM
对原理有了一定的认识之后,自然是动手实现一番了,
进一步思考
- 性能对比
首先,先来看一下性能,在诸多的

上图是对一个简单的

在接下来的测试中我们增加测试量。上图分别是使用原生操作、
再次审视Virtual DOM
框架存在的意义是什么?是提高性能?提高开发效率?亦或是其他用途,每个人对框架的理解不同,答案也不尽相同。但是不得不承认,存在框架的情况下,项目的可维护性有了极大的提高,而对于其他方面就要做出牺牲,比如性能。在上面的性能测试中,其实完全走入了一个误区,在测试中我们用到的原生的操作其实是“人为”地对操作进行优化之后的结果,而如果抛开人为优化的前提,最终的结果可能就不是这样了。
到此为止,再次审视
Virtual DOM 在牺牲部分性能的前提下,增加了可维护性,这也是很多框架的通性- 实现了对
DOM 的集中化操作,在数据改变时先对虚拟DOM 进行修改,再反映到真实的DOM 中,用最小的代价来更新DOM ,提高效率 - 打开了函数式
UI 编程的大门 - 可以渲染到
DOM 以外的端,比如ReactNative
结语
本文对
本文旨在让大家了解并认识