提高性能:
虚拟dom相当于在js和真实dom中间加了一个缓存,利用dom diff算法避免了没有必要的dom操作,从而提高性能
操作:
手动操作dom比较麻烦,还要考虑兼容问题 虽然有jq库简化dom操作但是项目复杂度越来越高,dom操作复杂提升,既要考虑dom操作还有考虑数据操作
mvvm框架:
为了简化dom复杂操作,mvvm框架解决了视图和数据同步问题
简化视图:
可以使用模板引擎,但是模板引擎没有解决跟踪数据变化问题于是虚拟dom出现了(数据改变后,无法获取上次状态,只有件页面上的元素删除,在重新创建,同时频繁操作dom性能非常低)
维护视图和状态的关系(虚拟dom会记录状态的变化,只需要更新状态变化的内容就可以了)
虚拟dom除了渲染dom以外,还可以实现渲染到其他的平台,可以实现服务段的渲染(ssr)
原生应用(react Native) 小程序等
什么是Snabbdom?
在学习Vue或React中,我们了解最多的就是虚拟DOM,虚拟DOM可以看作是一颗模拟了DOM的Javascript树,主要是通过vnode实现一个无状态的组件,当组件状态发生变更时,就会触发 virtual-dom 数据的变化,然后使用虚拟节点树进行渲染,但是在渲染之前,会使用新生成的虚拟节点树和上一次生成的虚拟节点树进行对比,只渲染两者之间不同的部分。
创建项目,并安装parcel
//创建项目目录
md snabbdom-demo
// 进入项目目录
cd snabbdom-demo
// 创建package.json
npm init -y
//本地安装parcel
npm install parcel-bundler
配置package.json
中的scripts
’
"srcipts":{
"dev":"parcel index.html --open" , //open打开浏览器
"build":"parcel build index.html"
}
官方文档
https://github.com/snabbdom/snabbdom
下面先安装Snabbdom
.(这里最新版本有问题,可以先安装0.7.4)
npm install [email protected]
在项目的js
文件夹下的01- basicusage.js
文件中,添加如下代码:
import snabbdom from "snabbdom";
console.log(snabbdom);
以上代码的意思就是导入snabbdom
这个包,然后打印其内容。
项目的启动
npm run dev
这时,开启的端口号为1234
http://localhost:1234
在打开的浏览器中,查看控制台的输出,发现输出的内容为undefined
为什么输出的是undefined
呢?
这里我们需要查看对应的源码,
在node_modules/snabbdom/snabbdom.js
文件中,
我们可以看到在整个文件中,并没有使用export default
的方式进行导出,所以就不能使用import snabbdom
这种方式进行导入。同时在,源码中,我们可以看到导出了三项内容分别为h
,thunk
,init
.
所以,导入的代码修改成如下的形式
import {
h, thunk, init } from "snabbdom";
console.log(h, thunk, init);
Snabbdom
的核心仅提供最基本的功能,只导出了三个函数init()
,h( )
,thunk( )
init
函数是高阶函数,返回patch( )
h
函数返回虚拟节点VNode
,这个函数我们在使用Vue.js
的时候见过。
h()
函数用于创建虚拟DOM
,在Snabbdom
中用VNode
描述虚拟节点,也就是虚拟DOM
new Vue({
router,
render:h=>h(App)
}).$mount('#app')
thunk
函数是一种优化策略,可以在处理不可变数据时使用(用于优化复杂的视图)
下面我们来看一下Snabbdom
的基本使用,snabbdom.js中编写代码
import { h, thunk, init } from 'snabbdom'
/**
* // init方法返回值为patch函数,patch函数作用是对比两个vndoe的差异并更新到真实的DOM中。
* init函数的参数是一个数组,数组中的内容是模块。
*/
let patch = init([])
/**
* 创建虚拟dom
* 第一个参数:标签+选择器(id选择器或类选择器)
* 第二个参数:如果是字符串的话就是标签中的内容
*/
let vnode = h('div#container.cls', 'hello chejia')
//我们这里需要将创建的虚拟dom,最终要渲染到`index.html`中`container`这个div中,所以这里需要获取一下该div
let app = document.querySelector('#container')
/**
* 要想将虚拟DOM渲染到app中,需要用到patch函数。
* 我们知道patch函数的作用是对比两个vnode的差异来更新到真实的`DOM`中。
* 但是我们目前没有两个虚拟DOM.那么patch方法的第一个参数也可以是真实的DOM.patch方法会将真实的DOM转换成VNode.
* 第二个参数:为VNode
* 返回值为vnode
*/
let oldNode = patch(app, vnode)
// vnode = h('div', "hello xx")
// patch(oldNode, vnode)
在index.html中
Document
在上面的代码中,我们又创建了一个虚拟DOM
,vnode
。然后把这个vnode
与oldNode
进行对比,最后渲染到页面中。
在scr
目录下面在创建一个文件:02-basicusage.js
// 本案例实现的要求是:在div中设置子元素h1,p
import { h, init } from "snabbdom";
let patch = init([]);
// h函数的第二个参数可以是一个数组,在该数组中添加所要创建的子元素。
let vnode = h("div#container", [
h("h1", "hello world"),
h("p", "这是一个p标签"),
]);
let app = document.querySelector("#app");
patch(app, vnode);
同时还需要修改index.html
文件中的引入
常用模块
attributes:设置
DOM元素的属性,内部使用
setAttribute()来设置属性,处理布尔类型的属性(可以对布尔类型的属性作相应的判断处理,布尔类型的属性,我们比较熟悉的有
selected,
checked`等)。
props: 和attributes
模块类似,设置DOM
元素的属性element[attr]=value
,不处理布尔类型的属性。
class
: 切换样式类,注意:给元素设置类样式是通过sel
选择器。··
dataset
:设置HTML5
中的 data-*
的自定义属性
eventlisteners
: 注册和移除事件
style
:设置行内样式,支持动画(内部创建transitionend
事件),会增加额外的属性:delayed / remove / destroy
使用模块的步骤:
第一步:导入需要的模块
第二步:在init()
中注册模块
第三步:使用h
函数创建VNode
的时候,可以把第二个参数设置为对象(对象中是模块需要的数据,可以设置行内样式、事件等),其它参数往后移。
下面我们要实现的案例,就是给div
添加一个背景,同时为其添加一个单击事件,当然在div
中还要创建两个元素分别是h1
与p
.
import { init, h } from "snabbdom";
//导入模块
import style from "snabbdom/modules/style";
import eventlisteners from "snabbdom/modules/eventlisteners";
//注册模块
let patch = init([style, eventlisteners]);
// 使用h函数的第二个参数传入模块所需要的数据(对象)
let vnode = h(
"div",
{
style: {
backgroundColor: "red",
},
on: {
click: eventHandler,
},
},
[h("h1", "Hello Vue"), h("p", "这是p标签")]
);
function eventHandler() {
console.log("点击了我");
}
let app = document.querySelector("#app");
patch(app, vnode);