Vue虚拟DOM原理与snabbdom

虚拟DOM

JavaScript对象描述真实DOM

  • 真实DOM的属性很多,创建DOM节点开销很大
  • 虚拟DOM只是普通JavaScript对象,描述属性并不需要很多,创建开销很小
  • 手动操作DOM很难跟踪以前的DOM状态,而虚拟DOM可以跟踪上一次状态,维护程序的状态信息。
虚拟DOM的作用
  • 维护视图与状态的关系
  • 复杂视图情况下,可以提升渲染性能
  • 还可以实现跨平台渲染,如SSR、原生应用、小程序等
虚拟DOM开源库
  • Snabbdom
  • virtual-dom

Snabbdom

基本使用

snabbdom的几个核心函数

  • init:初始化函数,接收一个数组作为参数,返回patch函数
  • patch:用来对新旧两个Vnode进行比对,并将差异更新到真实DOM,返回新的Vnode
    • 接收两个参数,分别为需要比对的Vnode,其中可以是真实DOM节点(会被转换为Vnode再比对)
  • h:创建Vnode
基本流程
  • 使用init初始化,得到patch函数
  • 使用h函数创建Vnode
  • 获取挂载点DOM元素
  • 使用patch函数比对挂载点与Vnode,更新到DOM中,并记录当前Vnode(挂载点的DOM被替换为Vnode)
  • 之后需要再次更新DOM时,则创建新的Vnode,并传入patch中。
// div中放置子元素h1, p
import { init, h } from 'snabbdom'

let patch = init([])

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', '更新了')
  ])
  patch(oldVnode, vnode)
  //清空页面
  // patch(oldVnode, null),这种方式是错误的
  // 创建注释节点h('!')
  patch(oldVnode, h('!'))
}, 2000);
snabbdom中的模块

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

常用模块

官方提供了6个模块

  • attributes
    • 设置DOM元素的属性,使用的是setAttribute()
    • 处理布尔类型的属性
  • props
    • 设置DOM元素的属性,但使用element[attr] = value
    • 不处理布尔类型的属性
  • class
    • 切换类样式
    • 注意:给元素设置类样式是通过sel选择器
  • dataset
    • 设置data-*的自定义属性
  • eventlisteners
    • 注册和移除事件
  • style
    • 设置行内样式,支持动画
    • delayed/remove/destroy
使用模块

模块的使用类似于插件机制。

  • 导入需要的模块
  • init()中注册模块,init函数接收的数组参数就是来传入模块
  • 使用h()函数创建Vnode的时候,可以把第二个参数设置为对象(通过该对象来设置行内样式、事件等等),其他参数往后移
  • 模块按需导入
  • 模块实现的核心是基于Hooks
源码解读
看源码必备快捷键

这些快捷键鼠标右键弹出菜单也可以找到。

  • 光标移动到某个变量处,按F12快速定位到该变量的定义位置。
  • ALT + 左方向键,回到上次的代码位置
  • Ctrl + 单击,跳转到某个变量的定义处
  • 选中某个变量或方法名,按F12显示出该变量或方法的具体代码
源码分析

snabbdom的核心在于DOM树和元素节点的操作(创建、插入、删除),至于其他如样式、事件等操作,都分布到module中来处理。

  • h()函数:主要是根据传入的参数,调用vnode()函数,创建虚拟节点,返回虚拟节点
  • vnode()函数:根据传入的参数,返回一个JavaScript对象,基本上是由传入的参数组成的对象
  • patch(oldVnode, newVnode)函数:将Vnode渲染为真实DOM。是snabbdom的核心函数。
    • 打补丁,即把新节点变化的内容渲染到真实DOM,最后返回新节点(作为下次处理的旧节点)
    • 对比新旧节点是否相同(节点的key和sel是否相同
    • 如果不是相同节点,删除之前的内容,重新渲染
      • createElm用来创建DOM节点

createElm的执行流程示意图

  • 如果是相同节点,判断newVnode是否有text,如果有且和oldVnode的text不同,则直接更新文本内容
  • 如果newVnode有children,判断子节点是否有变化,判断子节点的过程使用的就是diff算法
  • diff算法只进行同层级比较
  • init()函数:接收modules和domApi两个参数,返回patch函数。
    • 遍历每个module的钩子函数,将其存储到cbs对象中等待后续调用。
  • patchVnode()函数:对比两个Vnode,更新DOM,如果Vnode相同,则调用updateChildren来对比子节点。
  • updateChildren()核心的diff算法,通过比较相同节点的子节点,来尽可能复用DOM节点,减少DOM节点的操作。这里通过key属性来更好地查找可能复用的DOM节点。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值