前言
Svelte和SolidJS的兴起重新非虚拟DOM框架的模式引入公众视野。到2024,虚拟dom好像被市场尝试慢慢抛弃,Vue正在构建无虚拟dom的版本.....
虚拟dom
优点
HTML 模板的高度抽象。受限于 html 语法限制,我们难以复用 HTML 节点;而使用 Virtual DOM,我们能够用 JS 描述 DOM 节点,从而带来了复用 HTML 模板的能力,提高效率。
跨平台。抽象出了 Virtual DOM 数据结构后,就可以适配 DOM 之外的渲染目标,如移动端(Weex)。
- 利用虚拟dom,可以将一些html的属性抽象成js属性,减少对对真实dom的读取或操作,从而提升性能。对JS的操作比dom的操作会更加灵活和拓展性更强
- uniapp等跨平台框架实现的核心。
缺点
- 首次渲染性能低,第一次需要全量构建虚拟dom。
- 运行时内存占用大,需要维护一份虚拟dom tree的内存副本。
- 生成虚拟dom所需的成本。
VUE与虚拟dom
生成时机
vue利用parser将template转成AST,然后经过优化,而后通过代码生成器生成render函数,执行生成虚拟dom。而后通过数据更换和监测,传入数据可生成实时的虚拟dom,进行patch->diff后生成dom。
数据结构
本点以下内容复制来自现代前端科技解析 ------ Virtual DOM - 404Forest
本文实现的 Virtual DOM 参考 Vue,结构简化,如下:
typescript
interface VNode {
type?: 'Element' | 'Text' | 'Comment',
tag?: string,
data?: VNodeData,
children?: Array<VNode>,
text?: string,
elm?: Node // 对应的真实 DOM 节点
}
interface VNodeData {
key?: string,
ref?: string,
events?: {
[key: string]: any
},
attrs?: {
[key: string]: any
},
rawAttrs?: {
[key: string]: any
},
directives?: {
[key: string]: any
}
}
template到render
转换前:
xml
<div class="container">
<!-- test -->
<button v-on:click="clickHandler">click me</button>
<ul :class="testClass" v-if="show">
<li v-for="city in arr" >{{city}}</li>
</ul>
</div>
转换后:
less
function render() {
with(this) {
return _c('Element', 'div', {attrs: {class: 'container',},events: {},}, [
_s(`
`),
_m(` test `),
_s(`
`),
_c('Element', 'button', {attrs: {},events: {click: clickHandler},}, [
_s(`click me`)
]),
_s(`
`),
// v-if
(show) ? _c('Element', 'ul', {attrs: {class: testClass,},events: {},}, [
_s(`
`),
// v-for
...(() => {
return arr.map(city => {
return _c('Element', 'li', {attrs: {},events: {},}, [_s(city)])
})
})(),
_s(`
`)]) : _e(),_s(`
`)
])
}
}
创建真实dom
ini
// 根据 VNode 创建真实 dom,并附着在 VNode.elm 属性上
function createDOM(node: VNode) {
let $node: Node
if(node.type === 'Element') {
$node = document.createElement(node.tag)
node.children.forEach(e => {
$node.appendChild(createDOM(e))
})
updateProps($node, node.data.attrs, {})
updateEvents($node, node.data.events, {})
}
if(node.type === 'Text') {
$node = document.createTextNode(node.text)
}
if(node.type === 'Comment') {
$node = document.createComment(node.text)
}
node.elm = $node
return $node
}
发展历程
Vue1.0的时候还是真实dom,Vue2.0去除了运行时的 HTML Parser,全面拥抱虚拟dom,Vue3.0又在尝试剥离虚拟dom,推出了Vue的蒸汽模式(Vapor Mode)。
Vue next--Vapor Mode
vuejs/core-vapor: Vue Vapor (no virtual DOM) Experimental repo. (github.com)
- 一个全新的编译策略
- 相同的模板语法,但编译后的代码性能更高。
- 利用 Template标签克隆元素 + 更精准的绑定,并且没有虚拟 DOM
只支持 Composition API 和带有
在应用级别,使用 Vapor Mode 编译的应用能够完全移除虚拟DOM运行时,只包含极简轻量的 @vue/reactivity 和 vapor 模式运行时辅助代码。这样一个应用的基础大小只有约6kb,相比目前含虚拟DOM的 Vue 3 应用的约50kb,缩减了88%。
