snabbdom的基本使用

前言

我们上一篇博客中介绍了虚拟DOM的基本概念及常见类库,其中常见类库有2种,snabbdom和virtual-dom,而snabbdom是vue2.x版本中所使用的,所以我们这片文章就主要来记录一下snabbdom的基本使用。

创建项目

  • 打包工具为了方便使用比较简单的parcel

  • 创建项目 并安装parcel

        mkdir snabb-demo // 在工作目录下建立snabb-demo子目录
        cd snabb-demo //进入snabb-demo 目录
        yarn init --yes //初始化一个package.json文件,也可以用npm init
        yarn add parcel-bundler --dev // 安装parcel
        
    
  • 创建目录结构,根目录下创建一个index.html,src目录下创建一个basicuse.js
    其中index.html内容如下:

    其中html代码如下:
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <title>snabbdom demo</title>
      </head>
      <body>
        <div id="app"></div>
      </body>
      <script src="./src/basicuse.js"></script>
      
    
  • 在package.json中配置脚本

    "scripts":{
        "dev": "parcel index.html --open", 
        "build":"parcel build index.html"
    }
    

导入snabbdom

snabbdom文档

  • 英文文档:https://github.com/snabbdom/snabbdom
  • 中文文档:https://github.com/coconilu/Blog/issues/152

安装snabbdom

```
yarn add snabbdom //即可安装sanbbdom,也可以安装指定版本0.7.4

```

导入snabbdom

  • 采用es module 方式导入snabbdom

文档给出的示例是 var snabbdom = require(‘snabbdom’)采用的是commonJS的模块化方式引入的;我们此处需要使用es module来进行导入,所以我们去node_modules中找到snabbdom中的snabbdom.js,发现它只导出了h、thunk、init三个函数,并且都没有采用export default导出,我们学习es6知道,如果没有采用 export default,我们用es module导入的时候就要用对象的形式 {} 去接收
所以我们这样导入:import {h,thunk,init} from 'snabbdom’即可完成导入

import {h,thunk,init} from 'snabbdom'
  • h函数的作用是生成一个虚拟dom,我们在vue中也在使用
  • init() 是一个高阶函数,返回一个patch()
  • thunk()是一种优化策略,主要优化复杂视图

snabbdom使用示例

创建一个hello world

basicuse.js代码如下:


import { h, init } from 'snabbdom'
// 此例主要实现一个hello world 实例

// init接收一个数组作为参数,该参数主要指定使用的模块列表,也可以为空
// 返回一个使用指定模块集的patch函数,此函数的作用主要是对比两个vnode的差异更新到真实DOM
let patch = init([])

// h函数
// 第一个参数:标签+选择器
// 第二个参数:如果是字符串的话就是标签中的内容
// 返回值:vnode虚拟dom
let vnode = h('div#container.cls','hello world')

let appDom = document.querySelector('#app')

// 然后利用patch函数进行内容比较与替换

// patch()函数
// 第一个参数:可以是dom元素,如果是dom则会自动将dom转换成vnode
// 第二个参数:vnode
// 返回值:vnode

let oldVnode = patch(appDom,vnode)

index.html内容

   <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <title>snabbdom demo</title>
      </head>
      <body>
        <div id="app"></div>
      </body>
      <script src="./src/basicuse.js"></script>
     </html>

执行yarn run dev 之后,可以看到浏览器页面中输出了hello world

效果图如下:

在这里插入图片描述

假设我们从服务器拿到了内容想要进行替换,可以进行如下操作即可完成

let ser = h('div',"hello snabbdom")
patch(oldVnode,ser)

页面就会输出hello snabbdom

创建一个带子元素的节点并进行替换

创建02-basicuse.js,内容如下

import { h, init } from 'snabbdom'

let patch = init([])

// h()中第二个参数如果是字符串,被认为是内容,如果是数组被任务是子元素
let vnode = h('div#container',[
    h('h1','hello snabbdom'),
    h('p','这是一个P标签')

])
let app = document.querySelector('#app')
let oldVnode = patch(app,vnode)

setTimeout(()=>{
    vnode = h('div#container',[
        h('h1','hello world'),
        h('p','这是一个新的p标签内容')
    
    ]);
    patch(oldVnode,vnode)
},2000)

index.html内容

   <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <title>snabbdom demo</title>
      </head>
      <body>
        <div id="app"></div>
      </body>
      <script src="./src/02-basicuse.js"></script>
     </html>

可以通过yarn run dev查看效果,最开始页面将id为app的div替换为一个含H1与P把标签的div,2秒后值改变为我们新设的值

如果想要清空节点内容,注意官网给出的方式是错误的

patch(oldVnode,null) //官网给出的错误清空方法

patch(oldVnode,h('!')) //正确清空节点的方法
//h('!') 创建一个注释节点替换之前节点内容

snabbdom 模块

snabbdom的核心库并不能处理元素的属性、样式、事件等,如果需要处理的话,可以使用模块来处理

常用模块

官方提供的模块有以下6个:

  • attributes
    • 设置dom元素的属性,使用setAttribute()
    • 会对布尔类型的属性进行判断
  • props
    • 和attributes模块类似,设置dom的属性,但是是以element[attr] = value的形式设置的
    • 不会处理布尔类型的属性
  • class
    • 切换类样式
  • dataset
    • 设置data-*的自定义属性
  • eventlisteners
    • 注册和移除事件
  • style
    • 设置行内样式、支持动画

模块使用

模块使用步骤
  • 导入需要的模块
  • 在init()中注册模块
  • 使用h()函数创建vnode时,可以把第二个参数设置为对象,其他参数后移
代码演示

创建03-basicuse.js,内容如下:




import { h, init } from 'snabbdom'
// 1、导入模块
import style from 'snabbdom/modules/style'
import eventlisteners from 'snabbdom/modules/eventlisteners'
// 2、注册模块
var patch = init([style,eventlisteners])

// 3、使用h()函数的第二个参数存放样式、事件等,其他参数后移
let vnode = h('div',{
    style:{
        backgroundColor:'red' // 如果两个单词采用驼峰写法
    },
    on:{
        click:addCount // 所有事件都写在on里
    }
},[
    h('h1','h1的内容,增加了背景色和click事件'),
    h('p','这是一个p标签')
])
// 创建一个div,背景色为红色且绑定click事件addCount
// 包含2个孩子节点,一个是p标签,一个是h1标签
function addCount(){
    alert('方法增加~')
}
let app = document.querySelector('#app')

let oldvNode = patch(app,vnode)

效果图如下:

在这里插入图片描述

总结一下:

  • 先引入模块 import x from ‘snabbdom/modules/xx’
  • init()初始化的时候是一个数组,可以传入我们需要的模块,也可不传
  • 我们在h()函数的第二个参数中,可以增加相关的配置,第二个参数是一个对象。
  • 如果我们需要给该节点增加子节点,则第三个参数为数组,若只是想添加节点内容则需要是字符串。