A virtual DOM library with a focus on simplicity, modularity, powerful features and performance.
Thanks to Browserstack for providing access to their great cross-browser testing tools.
Virtual DOM is awesome. It allows us to express our application's view as a function of its state. But existing solutions were way too bloated, too slow, lacked features, had an API biased towards OOP , and/or lacked features I needed.
Snabbdom consists of an extremely simple, performant, and extensible core that is only ≈ 200 SLOC. It offers a modular architecture with rich functionality for extensions through custom modules. To keep the core simple, all non-essential functionality is delegated to modules.
You can mold Snabbdom into whatever you desire! Pick, choose, and customize the functionality you want. Alternatively you can just use the default extensions and get a virtual DOM library with high performance, small size, and all the features listed below.
h
function for easily creating virtual DOM nodes.h
helper.import { init, classModule, propsModule, styleModule, eventListenersModule, h } 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 container = document.getElementById("container"); const vnode = h( "div#container.two.classes", { on: { click: () => console.log("div clicked") } }, [ h("span", { style: { fontWeight: "bold" } }, "This is bold"), " and this is just normal text", h("a", { props: { href: "/foo" } }, "I'll take you places!") ] ); // Patch into empty DOM element – this modifies the DOM as a side effect patch(container, vnode); const newVnode = h( "div#container.two.classes", { on: { click: () => console.log("updated div clicked") } }, [ h( "span", { style: { fontWeight: "normal", fontStyle: "italic" } }, "This is now italic type" ), " and this is still just normal text", h("a", { props: { href: "/bar" } }, "I'll take you places!") ] ); // Second `patch` invocation patch(vnode, newVnode); // Snabbdom efficiently updates the old view to the new state
The core of Snabbdom provides only the most essential functionality. It is designed to be as simple as possible while still being fast and extendable.
init
The core exposes only one single function init
. This init
takes a list of modules and returns a patch
function that uses the
specified set of modules.
import { classModule, styleModule } from "snabbdom"; const patch = init([classModule, styleModule]);
patch
The patch
function returned by init
takes two arguments. The first
is a DOM element or a vnode representing the current view. The second
is a vnode representing the new, updated view.
If a DOM element with a parent is passed, newVnode
will be turned
into a DOM node, and the passed element will be replaced by the
created DOM node. If an old vnode is passed, Snabbdom will efficiently
modify it to match the description in the new vnode.
Any old vnode passed must be the resulting vnode from a previous call
to patch
. This is necessary since Snabbdom stores information in the
vnode. This makes it possible to implement a simpler and more
performant architecture. This also avoids the creation of a new old
vnode tree.
patch(oldVnode, newVnode);
While there is no API specifically for removing a VNode tree from its mount point element, one way of almost achieving this is providing a comment VNode as the second argument to patch
, such as:
patch( oldVnode, h("!", { hooks: { post: () => { /* patch complete */ } } }) );
Of course, then there is still a single comment node at the mount point.
h
It is recommended that you use h
to create vnodes. It accepts a
tag/selector as a string, an optional data object, and an
optional string or an array of children.
import { h } from "snabbdom"; const vnode = h("div#container", { style: { color: "#000" } }, [ h("h1.primary-title", "Headline"), h("p", "A paragraph") ]);
fragment
(experimental)Caution: This feature is currently experimental and must be opted in. Its API may be changed without an major version bump.
const patch = init(modules, undefined, { experimental: { fragments: true } });
Creates a virtual node that will be converted to a document fragment containing the given children.
import { fragment, h } from "snabbdom"; const vnode = fragment(["I am", h("span", [" a", " fragment"])]);
toVNode
Converts a DOM node into a virtual node. Especially good for patching over pre-existing, server-side generated HTML content.
import { init, styleModule, attributesModule, h, toVNode } from "snabbdom"; const patch = init([ // Initialize a `patch` function with the modules used by `toVNode` attributesModule // handles attributes from the DOM node datasetModule, // handles `data-*` attributes from the DOM node ]); const newVNode = h("div", { style: { color: "#000" } }, [ h("h1", "Headline"), h("p", "A paragraph"), h("img", { attrs: { src: "sunrise.png", alt: "morning sunrise" } }) ]); patch(toVNode(document.querySelector(".container")), newVNode);
Hooks are a way to hook into the lifecycle of DOM nodes. Snabbdom offers a rich selection of hooks. Hooks are used both by modules to extend Snabbdom, and in normal code for executing arbitrary code at desired points in the life of a virtual node.
Name | Triggered when | Arguments to callback |
---|---|---|
pre | the patch process begins | none |
init | a vnode has been added | vnode |
create | a DOM element has been created based on a vnode | emptyVnode, vnode |
insert | an element has been inserted into the DOM | vnode |
prepatch | an element is about to be patched | oldVnode, vnode |
update | an element is being updated | oldVnode, vnode |
postpatch | an element has been patched | oldVnode, vnode |
destroy | an element is directly or indirectly being removed | vnode |
remove | an element is directly being removed from the DOM | vnode, removeCallback |
post | the patch process is done | none |
The following hooks are available for modules: pre
, create
,
update
, destroy
, remove
, post
.
The following hooks are available in the hook
property of individual
elements: init
, create
, insert
, prepatch
, update
,
postpatch
, destroy
, remove
.
To use hooks, pass them as an object to hook
field of the data
object argument.
h("div.row", { key: movie.rank, hook: { insert: (vnode) => { movie.elmHeight = vnode.elm.offsetHeight; } } });
init
hookThis hook is invoked during the patch process when a new virtual node has been found. The hook is called before Snabbdom has processed the node in any way. I.e., before it has created a DOM node based on the vnode.
insert
hookThis hook is invoked once the DOM element for a vnode has been inserted into the document and the rest of the patch cycle is done. This means that you can do DOM measurements (like using getBoundingClientRect in this hook safely, knowing that no elements will be changed afterwards that could affect the position of the inserted elements.
remove
hookAllows you to hook into the removal of an element. The hook is called
once a vnode is to be removed from the DOM. The handling function
receives both the vnode and a callback. You can control and delay the
removal with the callback. The callback should be invoked once the
hook is done doing its business, and the element will only be removed
once all remove
hooks have invoked their callback.
The hook is only triggered when an element is to be removed from its
parent – not if it is the child of an element that is removed. For
that, see the destroy
hook.
destroy
hookThis hook is invoked on a virtual node when its DOM element is removed from the DOM or if its parent is being removed from the DOM.
To see the difference between this hook and the remove
hook,
consider an example.
const vnode1 = h("div", [h("div", [h("span", "Hello")])]); const vnode2 = h("div", []); patch(container, vnode1); patch(vnode1, vnode2);
Here destroy
is triggered for both the inner div
element and the
span
element it contains. remove
, on the other hand, is only
triggered on the div
element because it is the only element being
detached from its parent.
You can, for instance, use remove
to trigger an animation when an
element is being removed and use the destroy
hook to additionally
animate the disappearance of the removed element's children.
Modules work by registering global listeners for hooks. A module is simply a dictionary mapping hook names to functions.
const myModule = { create: (oldVnode, vnode) => { // invoked whenever a new virtual node is created }, update: (oldVnode, vnode) => { // invoked whenever a virtual node is updated } };
With this mechanism you can easily augment the behaviour of Snabbdom. For demonstration, take a look at the implementations of the default modules.
This describes the core modules. All modules are optional. JSX examples assume you're using the jsx
pragma provided by this library.
The class module provides an easy way to dynamically toggle classes on
elements. It expects an object in the class
data property. The
object should map class names to booleans that indicate whether or
not the class should stay or go on the vnode.
h("a", { class: { active: true, selected: false } }, "Toggle");
In JSX, you can use class
like this:
<div class={{ foo: true, bar: true }} /> // Renders as: <div class="foo bar"></div>
Allows you to set properties on DOM elements.
h("a", { props: { href: "/foo" } }, "Go to Foo");
In JSX, you can use props
like this:
<input props={{ name: "foo" }} /> // Renders as: <input name="foo" /> with input.name === "foo"
Properties can only be set. Not removed. Even though browsers allow addition and deletion of custom properties, deletion will not be attempted by this module. This makes sense, because native DOM properties cannot be removed. And if you are using custom properties for storing values or referencing objects on the DOM, then please consider using data-* attributes instead. Perhaps via the dataset module.
Same as props, but set attributes instead of properties on DOM elements.
h("a", { attrs:
字节跳动发布的AI编程神器IDE
Trae是一种自适应的集成开发环境(IDE),通过自动化和多元协作改变开发流程。利用Trae,团队能够更快速、精确地编写和部署代码,从而提高编程效率和项目交付速度。Trae具备上下文感知和代码自动完成功能,是提升开发效率的理想工具。
AI小说写作助手,一站式润色、改写、扩写
蛙蛙写作—国内先进的AI写作平台,涵盖小说、学术、社交媒体等多场景。提供续写、改写、润色等功能,助力创作者高效优化写作流程。界面简洁,功能全面,适合各类写作者提升内容品质和工作效率。
全能AI智能助手,随时解答生活与工作的多样问题
问小白,由元石科技研发的AI智能助手,快速准确地解答各种生活和工作问题,包括但不限于搜索、规划和社交互动 ,帮助用户在日常生活中提高效率,轻松管理个人事务。
实时语音翻译/同声传译工具
Transly是一个多场景的AI大语言模型驱动的同声传译、专业翻译助手,它拥有超精准的音频识别翻译能力,几乎零延迟的使用体验和支持多国语言可以让你带它走遍全球,无论你是留学生、商务人士、韩剧美剧爱好者,还是出国游玩、多国会议、跨国追星等 等,都可以满足你所有需要同传的场景需求,线上线下通用,扫除语言障碍,让全世界的语言交流不再有国界。
一键生成PPT和Word,让学习生活更轻松
讯飞智文是一个利用 AI 技术的项目,能够帮助用户生成 PPT 以及各类文档。无论是商业领域的市场分析报告、年度目标制定,还是学生群体的职业生涯规划、实习避坑指南,亦或是活动策划、旅游攻略等内容,它都能提供支持,帮助用户精准表达,轻松呈现各种信息。
深度推理能力全新升级,全面对标OpenAI o1
科大讯飞的星火大模型,支持语言理解、知识问答和文本创作等多功能,适用于多种文件和业务场景,提升办公和日常生活的效率。讯飞星火是一个提供丰富智能服务的平台,涵盖科技资讯、图像创作、写 作辅助、编程解答、科研文献解读等功能,能为不同需求的用户提供便捷高效的帮助,助力用户轻松获取信息、解决问题,满足多样化使用场景。
一种基于大语言模型的高效单流解耦语音令牌文本到语音合成模型
Spark-TTS 是一个基于 PyTorch 的开源文本到语音合成项目,由多个知名机构联合参与。该项目提供了高效的 LLM(大语言模型)驱动的语音合成方案,支持语音克隆和语音创建功能,可通过命令行界面(CLI)和 Web UI 两种方式使用。用户可以根据需求调整语音的性别、音高、速度等参数,生成高质量的语音。该项目适用于多种场景,如有声读物制作、智能语音助手开发等。
AI助力,做PPT更简单!
咔片是一款轻量化在线演示设计工具,借助 AI 技术,实现从内容生成到智能设计的一站式 PPT 制作服务 。支持多种文档格式导入生成 PPT,提供海量模板、智能美化、素材替换等功能,适用于销售、教师、学生等各类人群,能高效制作出高品质 PPT,满足不同场景演示需求。
选题、配图、成文,一站式创作,让内容运营更高效
讯飞绘文,一个AI集成平台,支持写作、选题、配图、排版和发布。高效生成适用于各类媒体的定制内容,加速品牌传播,提升内容营销效果。
专业的AI公文写作平台,公文写作神器
AI 材料星,专业的 AI 公文写作辅助平台,为体制内工作人员提供高效的公文写作解决方案。拥有海量公文文库、9 大核心 AI 功能,支持 30 + 文稿类型生成,助力快速完成领导讲话、工作总结、述职报告等材料,提升办公效率,是体制打工人的得力写作神器。
最新AI工具、AI资讯
独家AI资源、AI项目落地
微信扫一扫关注公众号