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 相关文章推荐
Span元素的width属性无效果原因及解决方案
Jan 15 Javascript
js实现addClass,removeClass,hasClass的函数代码
Jul 13 Javascript
防止jQuery ajax Load使用缓存的方法小结
Feb 22 Javascript
jquery渐隐渐显的图片幻灯闪烁切换实现方法
Feb 26 Javascript
jQuery轻松实现表格的隔行变色和点击行变色的实例代码
May 09 Javascript
JavaScript数组的定义及数字操作技巧
Jun 06 Javascript
Bootstrap table中toolbar新增条件查询及refresh参数使用方法
May 18 Javascript
vue + any-touch实现一个iscroll 实现拖拽和滑动动画效果
Apr 08 Javascript
详解Vue 单文件组件的三种写法
Feb 19 Javascript
javaScript 实现重复输出给定的字符串的常用方法小结
Feb 20 Javascript
Vue中computed及watch区别实例解析
Aug 01 Javascript
基于JavaScript实现省市联动效果
Jun 22 Javascript
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
Win2003下APACHE+PHP5+MYSQL4+PHPMYADMIN 的简易安装配置
2006/11/18 PHP
php过滤HTML标签、属性等正则表达式汇总
2014/09/22 PHP
php中count获取多维数组长度的方法
2014/11/03 PHP
php使用Cookie控制访问授权的方法
2015/01/21 PHP
PHP聊天室简单实现方法详解
2018/12/08 PHP
PHP获取对象属性的三种方法实例分析
2019/01/03 PHP
js鼠标点击按钮切换图片-图片自动切换-点击左右按钮切换特效代码
2015/09/02 Javascript
高效的jquery数字滚动特效
2015/12/17 Javascript
实例详解Nodejs 保存 payload 发送过来的文件
2016/01/14 NodeJs
基于jquery实现下拉框美化特效
2016/02/02 Javascript
教你如何终止JQUERY的$.AJAX请求
2016/02/23 Javascript
jQuery on()方法绑定动态元素的点击事件无响应的解决办法
2016/07/07 Javascript
更靠谱的H5横竖屏检测方法(js代码)
2016/09/13 Javascript
D3.js实现直方图的方法详解
2016/09/25 Javascript
关于Jquery中的事件绑定总结
2016/10/26 Javascript
BootStrap 实现各种样式的进度条效果
2016/12/07 Javascript
vue select组件的使用与禁用实现代码
2018/04/10 Javascript
JS/jQuery实现超简单的Table表格添加,删除行功能示例
2019/07/31 jQuery
Weex开发之地图篇的具体使用
2019/10/16 Javascript
vue自动添加浏览器兼容前后缀操作
2020/08/13 Javascript
uniapp实现可以左右滑动导航栏
2020/10/21 Javascript
easy_install python包安装管理工具介绍
2013/02/10 Python
python将MongoDB里的ObjectId转换为时间戳的方法
2015/03/13 Python
Python将多个excel文件合并为一个文件
2018/01/03 Python
Pyinstaller将py打包成exe的实例
2018/03/31 Python
CSS3正方体旋转示例代码
2013/08/08 HTML / CSS
移动端html5模拟长按事件的实现方法
2018/09/30 HTML / CSS
amazeui 验证按钮扩展的实现
2020/08/21 HTML / CSS
城野医生官方海外旗舰店:风靡亚洲毛孔收敛水
2018/04/26 全球购物
买卖正宗运动鞋:GOAT
2019/12/06 全球购物
什么时候需要进行强制类型转换
2016/09/03 面试题
酒店中秋节活动方案
2014/01/31 职场文书
改革共识倡议书
2014/08/29 职场文书
2015年绩效考核工作总结
2015/05/23 职场文书
2016年三八红旗手先进事迹材料
2016/02/26 职场文书
简历中的自我评价应该这样写!
2019/07/12 职场文书