Virtual Dom 库
Virtual Dom
就是又普通的 js
对象来描述 Dom
对象,因为 Dom
对象是非常庞大,而且兼容性不好,虚拟 Dom
在复杂视图中可以提升性能还可以跨平台使用。
Snabbdom
和 virtual-dom
就是两个出名的虚拟 Dom
的开源库。而 vue
内部就是引入了 Snabbdom
,且它只有 200 行代码,易于学习,所以接下来要分析 Snabbdom
开源库。
创建项目
首先我们创建一个名为 snabbdom-demo
的项目目录,然后使用 npm
初始化项目,安装 parcel
打包工具:
js
npm init -y
npm install parcel-bundler -D
修改项目的 package.json
,配置 scripts
:
js
"scripts": {
"dev": "parcel index.html --open",
"build": "parcel build index.html"
},
最后在项目目录下添加 index.html
和 src/test1.js
文件,并在 index.html
中引入 src/test1.js
:
js
<body>
<div id="app"></div>
<script src="./src/test1.js"></script>
</body>
使用示例
学习 snabbdom
的第一步就是查看 snabbdom 文档,文档中介绍了很多方法的使用,我们重点看一下 init
和 h
方法的使用。
js
import { init } from 'snabbdom/build/package/init'
import { h } from 'snabbdom/build/package/h'
const patch = init([])
let vnode = h('div#container.cls', 'Hello World')
// #app 占位
let app = document.querySelector('#app')
let oldVnode = patch(app, vnode)
setTimeout(() => {
// 清除div中的内容,替换为空的注释节点
patch(oldVnode, h('!'))
}, 2000);
这里使用从
'snabbdom/build/package/init'
,而不是像文档中的使用'snabbdom/init'
是因为parcel
不支持package.json
中的exports
,如果是webpack5
是支持的。
运行界面
js
npm run dev
我们可以看到界面显示 Hello World
,元素为 <div id="container" class="cls">Hello World</div>
,然后两秒后页面清空,元素为 <!---->
。
代码分析
从代码和运行结果看,我们可以理解 init
和 h
函数的作用。
- init : 接收包含模块的数组,并返回一个具有指定功能的
patch
函数 - h:接收两个参数,第一个参数表示标签+选择器,第二个参数如果是字符串就是标签中的文本内容,如果是数组就是子元素。
js
let vnode = h('div#container', [
h('h1', 'Hello Snabbdom'),
h('p', '这是一个p')
])
- patch : 对比两个
vnode
,把两个vnode
的差异更新到真实Dom
上。第一个参数为旧的VNode
,可以vnode
也可以是真实DOM
元素,如果是真实DOM
元素,在内部会转换为vnode
。第二个参数为新的VNode
。返回新的VNode
,可以作为下一次patch
的旧的VNode
。
模块
Snabbdom
的核心库并不能处理 DOM
元素的属性、样式、事件等,可以通过注册 Snabbdom
提供的模块来实现。
Snabbdom
官方提供了6个模块:attributes
、props
、dataset
、class
、style
、eventlisteners
。
使用
js
// 1. 导入模块
import { styleModule } from 'snabbdom/build/package/modules/style'
import { eventListenersModule } from 'snabbdom/build/package/modules/eventlisteners'
// 2. 注册模块
const patch = init([
styleModule,
eventListenersModule
])
// 3. 使用h() 函数的第二个参数传入模块中使用的数据(对象)
let vnode = h('div', [
h('h1', { style: { backgroundColor: 'red' } }, 'Hello World'),
h('p', { on: { click: eventHandler } }, 'Hello P')
])