一、什么是虚拟dom
虚拟DOM 其实就是一棵以 JavaScript 对象 (VNode 节点) 作为基础的树,用对象属性来描述节点,实际上它只是一层对真实 DOM 的抽象。
**
//真实dom
<div id="app"> hello world</div>
//虚拟dom
{ tagName:'div', attrs:{id:'app' },children:[ 'hello world']}
二、为什么要使用虚拟dom
1、减少直接操作 dom 次数,从而提高程序性能
js 层面上,DOM 的操作并不慢。DOM 操作慢是慢在浏览器渲染的过程里,改变一行数据就要全部重新渲染,在大多数情况下虚拟 DOM 比 DOM 快,是因为需要更新的 DOM 节点要比原生 DOM 操作更新的节点少,浏览器重绘的时间更短。而且虚拟 DOM 的优势不在于单次的操作,用对比的算法,它可以将多次操作合并成一次操作,在大量、频繁的数据更新下,能够对视图进行合理、高效的更新。
真实的dom上有一堆的属性和方法,直接操作DOM的话性能会变得很慢。频繁操作虚拟DOM不存在性能问题,等数据全部更新完之后只会去更新真实dom树需要更新改变的地方
直接操作真的的DOM每操作一次就会导致一次重绘和回流。使用虚拟dom,页面的更新可以先全部反映在JS对象(虚拟DOM)上,操作内存中的JS对象的速度显然要更快,等更新完成后,再将最终的JS对象映射成真实的DOM,交由浏览器去绘制,从而提高性能!
2、跨平台
它的本质就是一个 JavaScript 对象,并不依赖真实平台环境,所以使它具有了跨平台的能力。它在浏览器上可以变成 DOM,在其他平台里也可以变成相应的渲染对象。同一VNode 节点可以渲染成不同平台上的对应的内容,比如:渲染在浏览器是 dom 元素节点,渲染在 Native( iOS、Android) 变为对应的控件、可以实现 SSR(Nuxt.js/Next.js)、原生应用(Weex/React Native)、小程序(mpvue/uni-app)等 、渲染到 WebGL 中等等。
Vue3 中允许开发者基于 VNode 实现自定义渲染器(renderer),以便于针对不同平台进行渲染
三、DOM diff
snabbdom一个虚拟 DOM 库,专注提供简单、模块性的体验,以及强大的功能和性能。
1、snabbdom基本使用
**
//安装
npm i snabbdom -D
//使用
import {
init,
classModule,
propsModule,
styleModule,
eventListenersModule,
h,
} from "snabbdom";
const patch = init([
// 通过传入模块初始化 patch 函数
classModule, // 开启 classes 功能
propsModule, // 支持传入 props,允许在dom上设置属性
styleModule, // 支持内联样式同时支持动画
eventListenersModule, // 添加事件监听
]);
// 创建虚拟节点
const myVnode=h('div',{props:{href:"https:/'/baidu.com"}, on: { click: someFn },style:{color:'red'}},'我是替换后的内容')
function someFn(){
console.log(myVnode);
}
//让虚拟节点上树
const container=document.getElementById('container')
patch(container,myVnode)
//获取到的虚拟节点的内容为下面的对象
//让虚拟节点上树
const container=document.getElementById('container')
patch(container,myVnode1)
//再次调用 `patch`
const myVnode2=h('div',{},'test2')
patch(myVnode1,myVnode2)
2、核心算法
h函数:把传入的节点信息转化为虚拟dom
**
//使用形式
h('div')
h('div','文字’)
h('div',{},'文字’)
h('div',{},[])
h('div',{},h())
//嵌套使用,得到有children的虚拟节点
h('ul',{},[
h('li',{},'牛奶‘),
h('li',{},'咖啡‘)
])
patch函数:把虚拟dom转化为真实dom
**
// 调用h函数
h('a',{props:{href:'https://baidu.com'}},'点击跳转')
//产生虚拟节点
{"sel":"a","data":{"props":{"href":"https://baidu.com"}},"text":"点击跳转"}
// 真正的dom节点
<a href="https://baidu.com">点击跳转</a>
四、Diff中key的作用
key是vnode唯一的标识,且是不可变的。比如在我们码代码的时候,设置学号、id、身份证号这些唯一的。
key的作用是为了更高效的更新虚拟dom(提升渲染效率),也就是diff算法。比如我们想在bc中间加一个D,key可以做节点的唯一标识,告诉diff在更改前后是同一个节点。找到正确的位置插入新节点。 注意不要用index,因为比如中间插入数据,导致数据流标号的更改,index就变了,不仅会降低key的效率。严重的话会直接渲染出错。