UNPKG

@vanilla-dom/core

Version:

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

518 lines (385 loc) 12.7 kB
# @vanilla-dom/core 专注于 Web 客户端渲染的高性能 DOM 库核心包,采用编译时优化 + 运行时渲染的分离架构。 ## 🎯 设计理念 - **编译时优化**:通过 Babel 插件将 JSX 转换为优化的 VNode 树结构 - **运行时轻量**:专注于高效的 DOM 创建与更新,核心包 < 10KB gzipped - **纯客户端**:专为 Web 浏览器环境优化,无 SSR 包袱 - **类型安全**:完整 TypeScript 支持,`.tsx` 文件开箱即用 ## 🔄 渲染流程 ```mermaid graph LR A["JSX/VNode"] --> B["组件识别"] B --> C["DOM 创建"] C --> D["ref 回调"] ``` **核心流程:** - **组件识别**:区分注册组件、函数组件、HTML元素 - **DOM 创建**:高效的 VNode 到真实 DOM 转换 - **ref 回调**:DOM 创建完成后的通知机制 > 📋 详细技术流程请参考 [RENDERING.md](./RENDERING.md) ## ✨ 核心特性 - 🚀 **高性能渲染**:接近手写 DOM 操作的性能 - 📦 **轻量级体积**:运行时 < 10KB gzipped,编译时优化 - 🔧 **完整工具集**:内置 DOM 操作工具函数和批量更新优化 - 📘 **TypeScript 优先**:全局 JSX 命名空间,无需手动导入 - 🎯 **精确更新**:只更新实际变化的 DOM 节点和属性 - ⚡ **零配置**:`.tsx` 文件无需手动导入即可使用 ## 安装 ```bash pnpm add @vanilla-dom/core ``` ## 快速开始 ### 基础使用 ```typescript 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: ```tsx // 无需导入,直接使用 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 渲染到指定容器。 ```typescript import { render } from '@vanilla-dom/core'; render(vnode, { container: document.getElementById('root')!, replace: false, // 是否替换容器内容,默认 false }); ``` #### `createDOMFromTree(vnode)` 将 VNode 树转换为 DOM 元素。 ```typescript import { createDOMFromTree, h } from '@vanilla-dom/core'; const vnode = h('div', { id: 'test' }, 'Hello'); const domElement = createDOMFromTree(vnode); ``` #### `updateDOM(oldVNode, newVNode, domNode)` 基于新旧 VNode 的差异更新 DOM。 ```typescript import { updateDOM } from '@vanilla-dom/core'; updateDOM(oldVNode, newVNode, existingDOMNode); ``` ### DOM 工具集 ```typescript 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 的辅助函数。 ```typescript 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)` 注册组件编码范式处理器。 ```typescript 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)` 检查组件是否属于已注册的编码范式。 ```typescript import { isRegisteredComponent } from '@vanilla-dom/core'; const isRegistered = isRegisteredComponent(MyComponent); console.log(isRegistered); // true 或 false ``` #### `getComponentPattern(component)` 获取组件所属的编码范式名称。 ```typescript import { getComponentPattern } from '@vanilla-dom/core'; const pattern = getComponentPattern(MyComponent); console.log(pattern); // 'MY_PATTERN' 或 null ``` #### `renderRegisteredComponent(component, props, children)` 渲染已注册编码范式的组件。 ```typescript import { renderRegisteredComponent } from '@vanilla-dom/core'; const vnode = renderRegisteredComponent(MyComponent, { prop: 'value' }, []); // 返回渲染后的 VNode 或 null ``` ### 组件支持 ```typescript 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 的渲染,通过范式注册实现组件的抽象。 #### 注册编码范式处理器 ```typescript 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 组件编码范式 ```typescript 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 标签字符串 第三方库可以扩展更多编码范式: ```typescript // 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 参考 ```typescript // 注册组件编码范式 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 ```json { "compilerOptions": { "jsx": "preserve", "jsxImportSource": "@vanilla-dom/core", "lib": ["DOM", "ES2020"], "module": "ESNext", "moduleResolution": "bundler" } } ``` ### Babel 配置 (babel.config.js) ```javascript module.exports = { presets: [['@babel/preset-typescript']], plugins: [ // 需要配合 @vanilla-dom/babel-plugin 使用 ['@vanilla-dom/babel-plugin'], ], }; ``` ## 类型定义 ```typescript 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 操作减少重排重绘 - **内存管理**:自动清理事件监听器和引用 ### 使用建议 ```typescript // 批量更新优化 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/babel-plugin`](../babel-plugin) - JSX 编译插件,将 JSX 转换为优化的 VNode 调用 - [`@vanilla-dom/widget`](../widget) - 组件开发编码范式,提供 Widget 类和 createWidget 函数 - [`@vanilla-dom/babel-preset-widget`](../babel-preset-widget) - Widget 开发预设,开箱即用的 Babel 配置 ## 🏗️ 架构说明 `@vanilla-dom/core` 是整个生态系统的基础设施: - **底层渲染引擎**:处理 VNode 到真实 DOM 的转换 - **DOM 工具集**:提供高性能的 DOM 操作函数 - **类型支持**:全局 JSX 命名空间和完整 TypeScript 类型 - **组件注册机制**:支持第三方组件编码范式库扩展 ## 🎯 使用场景 ### 基础层独立使用 `@vanilla-dom/core` 可以与 `@vanilla-dom/babel-plugin` 配合,提供完整的基础 JSX 支持: ```bash pnpm add @vanilla-dom/core @vanilla-dom/babel-plugin ``` **适合场景**: - 轻量级应用,需要最小运行时开销 - 性能敏感场景,希望手动控制渲染逻辑 - 作为其他组件库的底层基础设施 ### 与增强层配合使用 也可以与 `@vanilla-dom/widget` 配合,获得更好的开发体验: ```bash pnpm add @vanilla-dom/widget @vanilla-dom/babel-preset-widget ``` **适合场景**:复杂应用、团队开发、需要结构化组件模式 ## 许可证 MIT