什么是虚拟 DOM
虚拟DOM是对DOM的一层抽象,以Javascript对象(VNode节点)作为基础的树。
用对象的属性描述节点,最后通过一些方法将其映射为真实的环境上,因此可实现跨平台。
当前主流前端框架React、Vue以及各种小程序的界面渲染都是用到的虚拟DOM技术。
1 | <div id="app"> |
1 | const vNode = { |
该js对象就是虚拟 DOM 了,它以js对象的形式描述了 DOM 节点。后续如果有 DOM 操作,不会直接操作 DOM,而是先变更 js 对象,然后 使用 diff 算法计算出新老对象之间的差异。最后最小范围的直接替换掉有变化的节点。
虚拟 DOM 的优势
原生 DOM 因为浏览器厂商需要实现众多的规范(各种 HTML5 属性、DOM事件),即使创建一个空的 div 也要付出昂贵的代价。
1 | var arr = [] |
上面代码输出可以看出,即使创建一个简单的DIV,就需要为其添加314个属性,如果之后直接操作该 DIV,应该会消耗掉不少的资源。
而对于虚拟DOM,我们只需要创建和维护几个我们用得上的必要属性,之后无论是增删查改还是通过diff算法计算出差异点,都是能够大大提高其效率的。
因此使用虚拟DOM,我们能够以最小代价的预处理DOM,最后一步到位渲染成真实的DOM。
当然这并不是虚拟DOM的主要优势,因为即使是原生的DOM操作也可以人为的控制批量操作DOM,通过减少DOM操作能够最大限度的解决这个效能问题。
虚拟DOM的最大的优势在于它可以接受 Parser 解析转化,这意味着其实相当多的东西我们都可以在编译阶段解决,比如:xss 攻击过滤。
除此之外,因为虚拟DOM是一个用 js 对象描述的DOM抽象,所以只要为这个抽象实现一个与之对应的UI层的映射,那么就可以将其应用到相应的UI处理,这也是为什么虚拟DOM能够实现跨平台的原因了。
Vue使用虚拟DOM渲染页面的完整过程
1.挂载,通过调用Vue.prototype.$mount()方法实现Vue页面的挂载
2.通过编译器来解析Template模板之后,生成render函数
3.调用vm._render(),将render函数转换为虚拟DOM
4.调用vm._update(),将虚拟DOM渲染为真实DOM
Vue是通过修改数据来更新视图的,当某个数据被修改的时候,set方法会让闭包中的Dep调用notify通知所有订阅者Watcher
Watcher通过get方法执行_update
1 | // _update 的调用时机有两个,一个是发生在初次渲染阶段,另一个发生数据更新阶段。 |
diff算法
diff算法通过同层的树节点比较,时间复杂度只有O(n)
只有Vnode节点相同(是同一个节点)的时候才会进行节点比较(patchVnode),否则直接删除旧节点,添加新节点。
判断是否同一个节点的标准
1 | key相同 |
新老节点 patchVnode 过程
1 | 若 都是静态节点判断key是否相同(相同表示为同一节点),且新VNode是clone或是标记了once,则直接替换elm以及componentInstance。 |
updateChildren过程
1 | 新老节点的左右两边 子节点 各起一个变量标记(oldStartVnode, newStartVnode, oldEndVnode, oldEndVnode),遍历的过程两边的标记向中间靠拢,直到全部遍历完成。 |
updateChildren源码
1 | function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) { |