@typeset/vite-plugin-static-component

在 Vite 转换阶段执行静态组件,将生成的 HTML 内联到调用方的 JSX 中。适合把排版计算、静态列表生成等工作提前完成,让浏览器直接使用结果。

本包提供框架无关的编译核心。应用接入请使用 React 适配包Solid 适配包。这些包目前都是仓库内的私有 workspace 包,直接导出 TypeScript 源码。

工作方式

  1. 在框架 JSX 编译前,查找 .tsx 调用方导入的静态组件。
  2. 识别组件模块中从对应 runtime 导入的 staticComponent() 标记。
  3. 提取每个 JSX 调用的静态 props、文本 children 和 renderWrapper
  4. 创建独立的 Vite SSR 服务,执行适配层生成的虚拟模块,取得 HTML 字符串。
  5. 用 HTML 表达式替换 wrapper 参数的引用,再用 wrapper 的 JSX 替换原组件调用。
  6. 删除原静态组件 import,保留其使用到的样式和资源依赖。

例如,以下 Solid 调用:

<Greeting name="Typeset" renderWrapper={(html) => <section innerHTML={html} />} />

若组件生成 <p>Hello Typeset</p>,转换后的 JSX 等价于:

<section innerHTML={"<p>Hello Typeset</p>"} />

HTML 随调用方模块进入构建产物;插件不单独输出 HTML 页面。静态内容没有客户端组件实例,也不会恢复内部事件处理器。wrapper 仍由应用的框架正常编译,可以保留调用方的动态属性和事件。

开发模式也会在模块转换时执行组件。依赖修改后,当前实现通过整页刷新重新生成内容。

适配器接口

包入口导出 createStaticComponentPlugin(adapter): Plugin,以及 StaticComponentAdapterStaticComponentRenderModuleInput 两个类型。

StaticComponentAdapter 的字段如下:

字段 用途
pluginName Vite 插件名,也用于隔离虚拟模块命名空间
runtimeModule 标记函数的导入来源,分析器按完整模块名匹配
markerName 标记函数的导出名,现有适配器均为 staticComponent
createRenderModule(input) 返回虚拟 TSX 模块源码;执行后必须默认导出 HTML 字符串
createRenderPlugins?() 返回内部 SSR 服务所需的框架编译插件

createRenderModule 接收以下输入:

字段 内容
componentPath Vite 解析后的组件模块 ID
componentName 调用方使用的组件本地名称
componentExportName 具名导出的名称;默认导出时为 undefined
propsObjectSource 可放入对象字面量中的属性源码,包含属性间的逗号
childrenSource 已拼接并去除首尾空白的文本 children;这是文本值,不是 JS 表达式源码

实现适配器时可参考 ReactSolid。内部 SSR 服务只加载渲染插件及选定的外层配置,不会完整复用应用的插件列表。

输入约束

当前支持范围以 analyze.tsrewrite.ts 为准:

这些限制针对调用处的静态输入。组件内部可以进行计算、导入本地辅助模块,并由适配器支持异步执行;执行环境是服务端,无法访问调用方的浏览器状态。

样式和资源

分析器递归检查使用到的静态 import,收集 CSS、CSS Modules 和图片等资源,再通过虚拟资源模块保留客户端依赖。未使用的绑定和纯类型 import 不参与这条收集路径。

对需要出现在 HTML 中的图片 URL,使用显式 ?url 导入,例如:

import photoUrl from "./photo.png?url";

当前 HTML URL 重写只处理显式 URL 导入,匹配项目根目录对应的开发路径,再替换成由 Vite 管理的客户端 URL 引用。发现某种资源不等于支持其所有 URL 写法;自定义 base、根目录外资源和其他 query 组合需按实际项目验证。依赖收集也不覆盖任意动态 import 或重导出链。

组件直接返回字符串时,适配器将其视为 HTML,不再按普通文本转义。应由生成该字符串的代码负责内容转义。

开发与验证

在仓库根目录执行,环境要求 Node.js >= 24 和仓库指定的 pnpm:

pnpm --filter @typeset/vite-plugin-static-component test
pnpm --filter @typeset/vite-plugin-static-component exec tsc --noEmit

涉及渲染、资源或适配器接口时,同时运行集成测试。单元测试覆盖导入分析、wrapper 作用域替换和依赖映射;真实构建验证由集成测试包负责。