npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@vanilla-dom/core

v0.0.0-alpha-20250608081318

Published

轻量级 DOM 渲染引擎,VNode 到 DOM 转换

Readme

@vanilla-dom/core

专注于 Web 客户端渲染的高性能 DOM 库核心包,采用编译时优化 + 运行时渲染的分离架构。

🎯 设计理念

  • 编译时优化:通过 Babel 插件将 JSX 转换为优化的 VNode 树结构
  • 运行时轻量:专注于高效的 DOM 创建与更新,核心包 < 10KB gzipped
  • 纯客户端:专为 Web 浏览器环境优化,无 SSR 包袱
  • 类型安全:完整 TypeScript 支持,.tsx 文件开箱即用

🔄 渲染流程

graph LR
    A["JSX/VNode"] --> B["组件识别"]
    B --> C["DOM 创建"]
    C --> D["ref 回调"]

核心流程:

  • 组件识别:区分注册组件、函数组件、HTML元素
  • DOM 创建:高效的 VNode 到真实 DOM 转换
  • ref 回调:DOM 创建完成后的通知机制

📋 详细技术流程请参考 RENDERING.md

✨ 核心特性

  • 🚀 高性能渲染:接近手写 DOM 操作的性能
  • 📦 轻量级体积:运行时 < 10KB gzipped,编译时优化
  • 🔧 完整工具集:内置 DOM 操作工具函数和批量更新优化
  • 📘 TypeScript 优先:全局 JSX 命名空间,无需手动导入
  • 🎯 精确更新:只更新实际变化的 DOM 节点和属性
  • 零配置.tsx 文件无需手动导入即可使用

安装

pnpm add @vanilla-dom/core

快速开始

基础使用

import { h, render } from '@vanilla-dom/core';

// 使用 h 函数创建 VNode
const app = h(
  'div',
  { class: 'app' },
  h('h1', null, 'Hello Vanilla DOM!'),
  h('p', null, 'High-performance client-side rendering'),
);

// 渲染到页面
render(app, {
  container: document.getElementById('root')!,
});

JSX 支持

配置 TypeScript 和 Babel 后,可以直接使用 JSX:

// 无需导入,直接使用 JSX
function App() {
  return (
    <div className="app">
      <h1>Hello Vanilla DOM!</h1>
      <p>High-performance client-side rendering</p>
    </div>
  );
}

render(<App />, { container: document.getElementById('root')! });

API 参考

渲染引擎

render(vnode, options)

将 VNode 渲染到指定容器。

import { render } from '@vanilla-dom/core';

render(vnode, {
  container: document.getElementById('root')!,
  replace: false, // 是否替换容器内容,默认 false
});

createDOMFromTree(vnode)

将 VNode 树转换为 DOM 元素。

import { createDOMFromTree, h } from '@vanilla-dom/core';

const vnode = h('div', { id: 'test' }, 'Hello');
const domElement = createDOMFromTree(vnode);

updateDOM(oldVNode, newVNode, domNode)

基于新旧 VNode 的差异更新 DOM。

import { updateDOM } from '@vanilla-dom/core';

updateDOM(oldVNode, newVNode, existingDOMNode);

DOM 工具集

import {
  appendChild,
  batchUpdate,
  createElement,
  createTextNode,
  removeNode,
  setProperty,
} from '@vanilla-dom/core';

// 创建元素
const div = createElement('div');

// 批量更新优化
batchUpdate(() => {
  setProperty(div, 'className', 'updated');
  appendChild(div, createTextNode('New content'));
});

VNode 创建

h(type, props, ...children)

创建 VNode 的辅助函数。

import { h } from '@vanilla-dom/core';

const vnode = h(
  'div',
  { class: 'container', onClick: handleClick },
  h('span', null, 'Child 1'),
  'Text child',
  h('span', null, 'Child 2'),
);

组件编码范式注册 API

registerComponentPattern(patternName, handler)

注册组件编码范式处理器。

import { registerComponentPattern } from '@vanilla-dom/core';
import type { ComponentPatternHandler } from '@vanilla-dom/core';
import { h } from '@vanilla-dom/core';

const handler: ComponentPatternHandler = {
  // 检测组件是否属于此编码范式
  detect: (component: any) => {
    return component && component.__PATTERN_TYPE__ === 'MY_PATTERN';
  },

  // 渲染组件为 VNode
  render: (component: any, props: any, children: any[]) => {
    // 调用组件的渲染方法,返回 VNode
    const instance = new component(props);
    return instance.render();
  },
};

registerComponentPattern('MY_PATTERN', handler);

isRegisteredComponent(component)

检查组件是否属于已注册的编码范式。

import { isRegisteredComponent } from '@vanilla-dom/core';

const isRegistered = isRegisteredComponent(MyComponent);
console.log(isRegistered); // true 或 false

getComponentPattern(component)

获取组件所属的编码范式名称。

import { getComponentPattern } from '@vanilla-dom/core';

const pattern = getComponentPattern(MyComponent);
console.log(pattern); // 'MY_PATTERN' 或 null

renderRegisteredComponent(component, props, children)

渲染已注册编码范式的组件。

import { renderRegisteredComponent } from '@vanilla-dom/core';

const vnode = renderRegisteredComponent(MyComponent, { prop: 'value' }, []);
// 返回渲染后的 VNode 或 null

组件支持

import type { ComponentFunction } from '@vanilla-dom/core';
import { h } from '@vanilla-dom/core';

const Button: ComponentFunction = props => {
  return h(
    'button',
    {
      class: `btn ${props.variant}`,
      onClick: props.onClick,
    },
    props.children,
  );
};

// 使用组件
const app = h(
  Button,
  {
    variant: 'primary',
    onClick: () => console.log('clicked'),
  },
  'Click me',
);

组件编码范式注册

@vanilla-dom/core 提供了组件编码范式注册机制,支持第三方组件库通过运行时注册实现自定义的组件编码模式。core 包专注于 VNode 到 DOM 的渲染,通过范式注册实现组件的抽象。

注册编码范式处理器

import { h, registerComponentPattern } from '@vanilla-dom/core';
import type { ComponentPatternHandler } from '@vanilla-dom/core';

// 注册自定义组件编码范式
const handler: ComponentPatternHandler = {
  // 检测函数:判断组件是否属于此编码范式
  detect: (component: any) => {
    return component && component.__PATTERN_TYPE__ === 'CUSTOM_PATTERN';
  },

  // 渲染函数:将组件渲染为 VNode
  render: (component: any, props: any, children: any[]) => {
    // 创建组件实例并调用其渲染方法
    const instance = new component(props);
    return instance.render();
  },
};

registerComponentPattern('CUSTOM_PATTERN', handler);

使用示例:Class 组件编码范式

import { registerComponentPattern, h, render } from '@vanilla-dom/core';
import type { VNode } from '@vanilla-dom/core';

// 1. 定义组件基类
class Component {
  static __PATTERN_TYPE__ = 'CLASS_COMPONENT';
  constructor(public props: any) {}
  abstract render(): VNode;
}

// 2. 注册编码范式
registerComponentPattern('CLASS_COMPONENT', {
  detect: (component: any) => {
    return component.prototype instanceof Component ||
           component.__PATTERN_TYPE__ === 'CLASS_COMPONENT';
  },

  render: (ComponentClass: any, props: any, children: any[]) => {
    const instance = new ComponentClass({ ...props, children });
    return instance.render();
  }
});

// 3. 定义具体组件
class TodoList extends Component {
  render(): VNode {
    return h('div', { class: 'todo-list' },
      h('h2', null, 'Todo List'),
      ...this.props.items.map((item: any) =>
        h('div', { key: item.id }, item.text)
      )
    );
  }
}

// 4. 使用组件
const app = h(TodoList, {
  items: [
    { id: 1, text: 'Learn Vanilla DOM' },
    { id: 2, text: 'Build awesome apps' }
  ]
});

render(app, { container: document.getElementById('root')! });

内置编码范式

@vanilla-dom/core 内置支持以下组件编码范式:

  • 函数组件ComponentFunction 类型的函数
  • HTML 元素:原生 HTML 标签字符串

第三方库可以扩展更多编码范式:

// Widget 编码范式(由 @vanilla-dom/widget 提供)
registerComponentPattern('WIDGET_CLASS', {
  detect: (component: any) =>
    component.prototype && component.__COMPONENT_TYPE__ === 'WIDGET_CLASS',
  render: (WidgetClass: any, props: any, children: any[]) => {
    const instance = new WidgetClass(props);
    return instance.render();
  },
});

// Factory 编码范式示例
registerComponentPattern('FACTORY_FUNCTION', {
  detect: (component: any) =>
    typeof component === 'function' && component.__FACTORY_TYPE__,
  render: (factory: any, props: any, children: any[]) => {
    return factory(props, children);
  },
});

API 参考

// 注册组件编码范式
function registerComponentPattern(
  patternName: string,
  handler: ComponentPatternHandler,
): void;

// 检查组件是否属于已注册范式
function isRegisteredComponent(component: any): boolean;

// 获取组件所属的编码范式名称
function getComponentPattern(component: any): string | null;

// 渲染已注册编码范式的组件
function renderRegisteredComponent(
  component: any,
  props: any,
  children: any[],
): VNode | null;

interface ComponentPatternHandler {
  // 必需:检测函数
  detect: (component: any) => boolean;
  // 必需:渲染函数,返回 VNode
  render: (component: any, props: any, children: any[]) => VNode;
}

TypeScript 配置

tsconfig.json

{
  "compilerOptions": {
    "jsx": "preserve",
    "jsxImportSource": "@vanilla-dom/core",
    "lib": ["DOM", "ES2020"],
    "module": "ESNext",
    "moduleResolution": "bundler"
  }
}

Babel 配置 (babel.config.js)

module.exports = {
  presets: [['@babel/preset-typescript']],
  plugins: [
    // 需要配合 @vanilla-dom/babel-plugin 使用
    ['@vanilla-dom/babel-plugin'],
  ],
};

类型定义

interface VNode {
  type: string | ComponentFunction;
  props: Record<string, any> | null;
  children: VNodeChild[];
  key?: string | number;
}

type VNodeChild = VNode | string | number | boolean | null | undefined;

type ComponentFunction = (props: Record<string, any>) => VNode;

// 组件编码范式相关类型
interface ComponentPatternHandler {
  detect: (component: any) => boolean;
  render: (component: any, props: any, children: any[]) => VNode;
}

type ComponentPattern = string;

🚀 性能优化策略

编译时优化

  • 静态模板提取:识别静态 HTML 部分,生成可复用模板
  • 动态插值标记:标记需要运行时更新的节点位置
  • 事件优化:自动识别可委托的事件处理

运行时策略

  • 模板克隆:复用静态模板,减少 DOM 创建开销
  • 精确更新:只更新变化的节点属性/内容
  • 批量操作:使用 batchUpdate 自动合并 DOM 操作减少重排重绘
  • 内存管理:自动清理事件监听器和引用

使用建议

// 批量更新优化
batchUpdate(() => {
  setProperty(element, 'className', 'updated');
  appendChild(element, createTextNode('New content'));
  setProperty(element, 'data-id', '123');
});

// 组件缓存 - 相同 props 的组件会复用渲染结果
const Button = props => h('button', props, props.children);

浏览器支持

  • Chrome >= 60
  • Firefox >= 55
  • Safari >= 12
  • Edge >= 79

🔗 相关包

🏗️ 架构说明

@vanilla-dom/core 是整个生态系统的基础设施:

  • 底层渲染引擎:处理 VNode 到真实 DOM 的转换
  • DOM 工具集:提供高性能的 DOM 操作函数
  • 类型支持:全局 JSX 命名空间和完整 TypeScript 类型
  • 组件注册机制:支持第三方组件编码范式库扩展

🎯 使用场景

基础层独立使用

@vanilla-dom/core 可以与 @vanilla-dom/babel-plugin 配合,提供完整的基础 JSX 支持:

pnpm add @vanilla-dom/core @vanilla-dom/babel-plugin

适合场景

  • 轻量级应用,需要最小运行时开销
  • 性能敏感场景,希望手动控制渲染逻辑
  • 作为其他组件库的底层基础设施

与增强层配合使用

也可以与 @vanilla-dom/widget 配合,获得更好的开发体验:

pnpm add @vanilla-dom/widget @vanilla-dom/babel-preset-widget

适合场景:复杂应用、团队开发、需要结构化组件模式

许可证

MIT