Vue.js 官方为何偏爱 Snabbdom?

发表时间: 2023-10-31 06:20

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!

今天给大家带来的主题是 Snabbdom,即一个虚拟 DOM 库,专注于简单性、模块化、强大的功能和性能。话不多说,直接开始。

1.什么是 Snabbdom

什么是虚拟 DOM

虚拟 DOM 允许开发者将应用程序的视图表达为其状态的函数。

Snabbdom 由一个极简、高性能和可扩展的核心组成,该核心仅为 ≈ 200 SLOC(single line of code)。 Snabbdom 提供了一个模块化架构,具有丰富的功能,可通过自定义模块进行扩展。 同时,为了保持 Snabbdom 核心足够简单,所有非必要的功能都委托给模块。

下面是 Snabbdom 的官方描述:

A virtual DOM library with focus on simplicity, modularity, powerful features and performance.

开发者可以将 Snabbdom 塑造成任何想要的东西, 也支持挑选、定制任何想要的功能。 当然,开发者也可以只使用 Snabbdom 默认扩展并获得具有高性能、小尺寸和下面列出的所有功能的虚拟 DOM 库。

目前 Snabbdom 在 Github 上通过 MIT 协议开源,有超过 10.9k 的 star、1.1k 的 fork、代码贡献者 120+,NPM周平均下载量20K左右,妥妥的前端优质开源项目

Snabbdom 核心功能

  • 大约 200 个 SLOC,开发者可以轻松阅读整个核心并完全理解它是如何工作的。
  • 可通过模块扩展 Snabbdom
  • Snabbdom 包含一组丰富的 hooks 可用,包括每个 vnode 和全局的模块,用于 diff 和 patch 的任何部分。
  • 优秀的性能, Snabbdom 是最快的虚拟 DOM 库之一。
  • 具有等同于减少/扫描功能(Reduce/scan function)的功能签名的补丁功能。 允许更容易地与 FRP 库集成。

模块中的功能

  • h 函数,用于轻松创建虚拟 DOM 节点。
  • SVG 仅适用于 h 工具函数。
  • 用于制作复杂 CSS 动画的功能。
  • 强大的事件监听器功能。
  • Thunks 进一步优化 diff 和 patch 过程。
  • JSX 支持,包括 TypeScript 类型

第三方功能

  • 由 snabbdom-to-html 提供的服务器端 HTML 输出。
  • 使用 snabbdom-helpers 创建紧凑的虚拟 DOM。
  • 使用 snabby 的模板字符串支持。
  • 使用 snabbdom-looks-like 的虚拟 DOM 断言

2.Snabbdom核心API介绍

Snabbdom 的核心只提供最基本的功能,被设计得尽可能简单,快速、可扩展。

init

Snabbdom 核心只公开一个函数 init,是一个高阶函数,此 init 获取模块列表并返回使用指定模块集的 patch 函数。

import { classModule, styleModule } from "snabbdom";const patch = init([classModule, styleModule]);

注意,在 Vue.js 中,patch 算法就是 Vue的虚拟 DOM 渲染成真正 DOM 的流程

patch

init 返回的 patch 函数有两个参数, 第一个是代表当前视图的 DOM 元素或 vnode,第二个是代表新的更新视图的 vnode。

如果传递了一个带有父元素的 DOM 元素,newVnode 将变成一个 DOM 节点,传递的元素将被创建的 DOM 节点替换。 如果传递了旧的 vnode,Snabbdom 将有效地修改它以匹配新 vnode 中的描述。

传递的任何旧 vnode 必须是先前调用 patch 的结果 vnode。 这是必要的,因为 Snabbdom 将信息存储在 vnode 中。 这使得实现更简单、性能更高的架构成为可能,也避免了创建新的旧 vnode 树。

patch(oldVnode, newVnode);

Unmounting

虽然没有专门用于从挂载点元素(mount point element)中删除 VNode 树的 API,但可以实现此目的的一种方法是提供注释 VNode 作为 patch 的第二个参数,例如:

patch(  oldVnode,  h("!", {    hooks: {      post: () => {        /* patch complete */      },    },  }));

当然,那么挂载点还是有一个单独的注释节点。

h

建议开发者使用 h 创建 vnode。它接受一个标签/选择器作为一个字符串、一个可选的数据对象和一个可选的字符串或子数组。

import { h } from "snabbdom";const vnode = h("div", { style: { color: "#000" } }, [  h("h1", "Headline"),  h("p", "A paragraph"),]);

fragment (experimental)

注意:此功能目前处于试验阶段,必须选择性加入。它的 API 可能会在没有主版本更新的情况下更改。

const patch = init(modules, undefined, {  experimental: {    fragments: true,  },});

创建一个虚拟节点,该节点将转换为包含给定子项的文档片段(document fragment)。

import { fragment, h } from "snabbdom";const vnode = fragment(["I am", h("span", [" a", " fragment"])]);

tovnode

将 DOM 节点转换为虚拟节点,特别适用于 patch 预先存在的服务器端生成的内容。

import {  init,  classModule,  propsModule,  styleModule,  eventListenersModule,  h,  toVNode,} from "snabbdom";const patch = init([  // Init patch function with chosen modules  classModule, // makes it easy to toggle classes  propsModule, // for setting properties on DOM elements  styleModule, // handles styling on elements with support for animations  eventListenersModule, // attaches event listeners]);const newVNode = h("div", { style: { color: "#000" } }, [  h("h1", "Headline"),  h("p", "A paragraph"),]);patch(toVNode(document.querySelector(".container")), newVNode);

Hooks

Hooks 是一种 hook DOM 节点生命周期的方法。 Snabbdom 提供了丰富的 Hooks 选择。Hooks 既被模块用于扩展 Snabbdom,也被用于在虚拟节点生命周期中的所需时间点执行任意代码的普通代码。

以下 Hooks 可用于模块:pre、create、update、destroy、remove、post。 以下 Hooks 在单个元素的 hook 属性中可用:init、create、insert、prepatch、update、postpatch、destroy、remove。

要使用 Hooks,可以将它们作为对象传递给数据对象参数的 hook 字段。

h("div.row", {  key: movie.rank,  hook: {    insert: (vnode) => {      movie.elmHeight = vnode.elm.offsetHeight;    },  },});

当虚拟节点的 DOM 元素从 DOM 中移除或者其父节点从 DOM 中移除时,将在虚拟节点上调用 destroy hooks。 要了解 destroy hook 和 remove hook 之间的区别,请考虑下面的示例。

const vnode1 = h("div", [h("div", [h("span", "Hello")])]);const vnode2 = h("div", []);patch(container, vnode1);patch(vnode1, vnode2);

这里 destroy 被内部 div 元素和它包含的 span 元素触发。 另一方面,remove 仅在 div 元素上触发,因为它是唯一与其父元素分离的元素。

例如,开发者可以使用 remove 在元素被删除时触发动画,并使用 destroy hook 为已删除元素的子元素设置消失动画。

3.谁在使用Snabbdom

Snabbdom 是一个底层虚拟 DOM 库,对于如何构建应用程序不做要求, 以下是使用 Snabbdom 构建应用程序的一些示例。

  • Vue.js :使用 snabbdom 的 fork 分支
  • Cycle.js :用于更简洁代码的功能性和反应性 JavaScript 框架使用 Snabbdom
  • WebCell :基于 JSX 和 TypeScript 的 Web 组件引擎
  • Mark Text : 基于 Snabbdom 构建的实时预览 Markdown 编辑器。
  • kaiju : snabbdom 之上的有状态组件和可观察对象
  • Cyclow : JavaScript 的响应式前端框架使用 Snabbdom

4.本文总结

本文主要和大家介绍下 Snabbdom,即一个虚拟 DOM 库,专注于简单性、模块化、强大的功能和性能。相信通过本文的阅读,大家对 Snabbdom 会有一个初步的了解。

因为篇幅有限,文章并没有过多展开,如果有兴趣,可以在我的主页继续阅读,同时文末的参考资料提供了大量优秀文档以供学习。最后,欢迎大家点赞、评论、转发、收藏!


参考资料

https://github.com/vuejs/

https://github.com/snabbdom/c

https://javascript.plainenglish.io/snabbdom-8a1fa7f9105d

https://www.npmjs.com/package/snabbdom