vue 虚拟DOM的原理


Posted in Javascript onOctober 03, 2020

为什么需要虚拟DOM?

如果对前端工作进行抽象的话,主要就是维护状态和更新视图,而更新视图和维护状态都需要DOM操作。其实近年来,前端的框架主要发展方向就是解放DOM操作的复杂性。

运行js的速度是很快的,大量的操作DOM就会很慢,时常在更新数据后会重新渲染页面,这样造成在没有改变数据的地方也重新渲染了DOM 节点,这样就造成了很大程度上的资源浪费。

在jQuery出现以前,我们直接操作DOM结构,这种方法复杂度高,兼容性也较差。有了jQuery强大的选择器以及高度封装的API,我们可以更方便的操作DOM,jQuery帮我们处理兼容性问题,同时也使DOM操作变得简单。

但是聪明的程序员不可能满足于此,各种MVVM框架应运而生,有angularJS、avalon、vue.js等,MVVM使用数据双向绑定,使得我们完全不需要操作DOM了,更新了状态,视图会自动更新。更新了视图数据状态也会自动更新,可以说MVVM使得前端的开发效率大幅提升。但是其大量的事件绑定使得其在复杂场景下的执行性能堪忧,有没有一种兼顾开发效率和执行效率的方案呢?由此引入Virtual DOM(虚拟DOM)。

利用在内存中生成与真实DOM与之对应的数据结构,这个在内存中生成的结构称之为虚拟DOM 。

当数据发生变化时,能够智能地计算出重新渲染组件的最小代价并应用到DOM操作上。

Virtual DOM 算法

所谓的 Virtual DOM 算法。包括几个步骤:

1.用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中;

2.当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异;

3.把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。

Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。可以类比 CPU 和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存。

既然 DOM 这么慢,我们就在它们 JS 和 DOM 之间加个缓存。CPU(JS)只操作内存(Virtual DOM),最后的时候再把变更写入硬盘(DOM)。

所谓的virtual dom,也就是虚拟节点。它通过js的Object对象模拟DOM中的节点,然后再通过特定的render方法将其渲染成真实的DOM节点 dom。diff 则是通过JS层面的计算,返回一个patch对象,即补丁对象,在通过特定的操作解析patch对象,完成页面的重新渲染。

vue 虚拟DOM的原理

比较两棵虚拟DOM树的差异

比较两棵DOM树的差异是 Virtual DOM 算法最核心的部分,这也是所谓的 Virtual DOM 的 diff 算法。

两个树的完全的 diff 算法是一个时间复杂度为 O(n^3) 的问题。但是在前端当中,你很少会跨越层级地移动DOM元素。所以 Virtual DOM 只会对同一个层级的元素进行对比:

vue 虚拟DOM的原理

上面的div只会和同一层级的div对比,第二层级的只会跟第二层级对比。这样算法复杂度就可以达到 O(n)。

在实际的代码中,会对新旧两棵树进行一个深度优先的遍历,这样每个节点都会有一个唯一的标记,如下图所示:

vue 虚拟DOM的原理

Virtual DOM 算法实现

Virtual DOM 算法得实现主要是用三个函数:element,diff,patch。然后就可以实际的进行使用,如下面代码所示:

// 1. 构建虚拟DOM
var tree = el('div', {'id': 'container'}, [
  el('h1', {style: 'color: blue'}, ['simple virtal dom']),
  el('p', ['Hello, virtual-dom']),
  el('ul', [el('li')])
])

// 2. 通过虚拟DOM构建真正的DOM
var root = tree.render()
document.body.appendChild(root)

// 3. 生成新的虚拟DOM
var newTree = el('div', {'id': 'container'}, [
  el('h1', {style: 'color: red'}, ['simple virtal dom']),
  el('p', ['Hello, virtual-dom']),
  el('ul', [el('li'), el('li')])
])

// 4. 比较两棵虚拟DOM树的不同
var patches = diff(tree, newTree)

// 5. 在真正的DOM元素上应用变更
patch(root, patches)

diff算法

用 三大策略 将O(n^3)复杂度 转化为 O(n)复杂度

  • 策略一(tree diff):

Web UI中DOM节点跨层级的移动操作特别少,可以忽略不计。

  • 策略二(component diff):

拥有相同类的两个组件 生成相似的树形结构,

拥有不同类的两个组件 生成不同的树形结构。

  • 策略三(element diff):

对于同一层级的一组子节点,通过唯一id区分。

tree diff

(1)通过updateDepth对Virtual DOM树进行层级控制。
(2)对树分层比较,两棵树只对同一层次节点进行比较。如果该节点不存在时,则该节点及其子节点会被完全删除,不会再进一步比较。
(3)只需遍历一次,就能完成整棵DOM树的比较。

vue 虚拟DOM的原理

diff只简单考虑同层级的节点位置变换,如果是跨层级的话,只有创建节点和删除节点的操作。

vue 虚拟DOM的原理

如上图所示,以A为根节点的整棵树会被重新创建,而不是移动,因此官方建议不要进行DOM节点跨层级操作,可以通过CSS隐藏、显示节点,而不是真正地移除、添加DOM节点。

以上就是vue 虚拟DOM的原理的详细内容,更多关于vue 虚拟DOM的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
js中document.getElementByid、document.all和document.layers区分介绍
Dec 08 Javascript
分析了一下JQuery中的extend方法实现原理
Feb 27 Javascript
JavaScript实现下拉列表框数据增加、删除、上下排序的方法
Aug 11 Javascript
利用jQuery设计一个简单的web音乐播放器的实例分享
Mar 08 Javascript
使用JS实现图片展示瀑布流效果的实例代码
Sep 12 Javascript
jQuery实现带遮罩层效果的blockUI弹出层示例【附demo源码下载】
Sep 14 Javascript
JS实现获取来自百度,Google,soso,sogou关键词的方法
Dec 21 Javascript
基于vue,vue-router, vuex及addRoutes进行权限控制问题
May 02 Javascript
Angular6 写一个简单的Select组件示例
Aug 20 Javascript
读懂CommonJS的模块加载
Apr 19 Javascript
JS实现水平移动与垂直移动动画
Dec 19 Javascript
手动实现vue2.0的双向数据绑定原理详解
Feb 06 Vue.js
vue使用video插件vue-video-player的示例
Oct 03 #Javascript
区分vue-router的hash和history模式
Oct 03 #Javascript
Vue双向数据绑定(MVVM)的原理
Oct 03 #Javascript
Chrome插件开发系列一:弹窗终结者开发实战
Oct 02 #Javascript
js通过canvas生成图片缩略图
Oct 02 #Javascript
JS检测浏览器开发者工具是否打开的方法详解
Oct 02 #Javascript
JavaScript检测是否开启了控制台(F12调试工具)
Oct 02 #Javascript
You might like
《破坏领主》销量已超100万 未来将继续开发新内容
2020/03/08 其他游戏
PHP4实际应用经验篇(4)
2006/10/09 PHP
mysql+php分页类(已测)
2008/03/31 PHP
深入解析php之sphinx
2013/05/15 PHP
PHP 获取文件权限函数介绍
2013/07/11 PHP
PHP实现递归的三种方法
2020/07/04 PHP
使用JQUERY Tabs插件宿主IFRAMES
2010/01/01 Javascript
JavaScript原型继承之基础机制分析
2011/08/26 Javascript
Jquery 实现表格颜色交替变化鼠标移过颜色变化实例
2013/08/28 Javascript
nodejs npm package.json中文文档
2014/09/04 NodeJs
初步使用Node连接Mysql数据库
2016/03/03 Javascript
详解微信小程序——自定义圆形进度条
2016/12/29 Javascript
js遮罩效果制作弹出注册界面效果
2017/01/25 Javascript
vue-router路由参数刷新消失的问题解决方法
2017/06/17 Javascript
vue-test-utils初使用详解
2019/05/23 Javascript
BootStrap前端框架使用方法详解
2020/02/26 Javascript
基于js实现数组相邻元素上移下移
2020/05/19 Javascript
全局安装 Vue cli3 和 继续使用 Vue-cli2.x操作
2020/09/08 Javascript
Javascript新手入门之字符串拼接与变量的应用
2020/12/03 Javascript
在Python的Django框架下使用django-tagging的教程
2015/05/30 Python
Python配置mysql的教程(推荐)
2017/10/13 Python
python下载文件记录黑名单的实现代码
2017/10/24 Python
Python实现一个简单的验证码程序
2017/11/03 Python
python 3.6.5 安装配置方法图文教程
2018/09/18 Python
Python高斯消除矩阵
2019/01/02 Python
python通过TimedRotatingFileHandler按时间切割日志
2019/07/17 Python
python中时间转换datetime和pd.to_datetime详析
2019/08/11 Python
H5混合开发app如何升级的方法
2018/01/10 HTML / CSS
Groupon法国官方网站:特卖和网上购物高达-70%
2019/09/02 全球购物
留学自荐信的技巧
2013/10/17 职场文书
信息系统专业个人求职信范文
2013/12/07 职场文书
网络编辑职责
2014/03/01 职场文书
少先队辅导员事迹材料
2014/12/24 职场文书
焦裕禄纪念馆观后感
2015/06/09 职场文书
大学生,三分钟即兴演讲稿
2019/07/22 职场文书
详解Mysql和Oracle之间的误区
2021/05/18 MySQL