组件 API

项目组件位于 vp/components/。构建时会加载这些组件;如果组件提供运行时增强代码,会被打包成独立组件脚本,而不是合并进 runtime.js

目录约定

组件可以是单文件,也可以是带 index.ts 的目录:

  • vp/
    • components/
      • badge.ts
      • callout/
        • index.ts

VanillaPress 会加载 vp/components/*.tsvp/components/*.jsvp/components/*/index.tsvp/components/*/index.js

组件约定

组件模块导出一个组件对象:

TypeScript
import type { MarkdownComponentDefinition } from 'vanilla-press'export default { name: 'badge', install(md, { markComponent, escapeHtml }) { md.block.ruler.before( 'paragraph', 'badge', (state, startLine, endLine, silent) => { const start = state.bMarks[startLine] const end = state.eMarks[startLine] const line = state.src.slice(start, end).trim() if (!line.startsWith(':::badge')) return false if (silent) return true const content = line.replace(/^:::badge/, '').trim() const token = state.push('badge', 'span', 0) token.content = content markComponent(state.env, 'badge') state.line = startLine + 1 return true } ) md.renderer.rules.badge = (tokens, index) => `<span class="vp-badge" data-vp-component="badge">${escapeHtml(tokens[index].content)}</span>` }, init(root) { root .querySelectorAll( '[data-vp-component="badge"]:not([data-vp-ready="true"])' ) .forEach((node) => { node.setAttribute('data-vp-ready', 'true') }) },} satisfies MarkdownComponentDefinition

也可以使用命名导出。VanillaPress 会按下面顺序解析组件模块:

TypeScript
// 方式一:默认导出组件对象export default { name: 'badge', install() {}, init() {},} satisfies MarkdownComponentDefinition// 方式二:导出 component 对象export const component = { name: 'badge', install() {}, init() {},} satisfies MarkdownComponentDefinition// 方式三:直接命名导出字段export const name = 'badge'export const dependsOn = ['tabs']export function install(md, context) {}export function init(root, config) {}

组件名必须匹配 /^[A-Za-z][\w-]*$/,并且在 vp/components/ 中不能重复。

Markdown 使用

Markdown
:::badge Stable

构建期 install 用于注册 Markdown 语法;当页面使用组件时,需要调用 markComponent(env, name)。运行时 init 是可选的;如果存在,会被打包成 dist/public/组件名.hash.js,并且只在使用了该组件的 HTML 页面中加载。

组件 init 中静态导入的本地 npm 依赖,会一起打包进该组件脚本文件,不会进入全局 runtime.js

如果组件没有 init,它只参与 Markdown 构建,不会生成浏览器脚本。

运行时规则

  • 渲染后的根元素必须包含 data-vp-component="<name>"
  • init(root, config)root 是当前文档根节点,config 是站点配置。
  • 运行时代码只扫描传入的 root 范围,不要默认全局操作不相关节点。
  • 已标记 data-vp-ready="true" 的元素必须跳过。
  • 初始化完成后,需要写入 data-vp-ready="true"
  • 如果组件依赖其他组件先初始化,通过 dependsOn 声明。
  • 组件模块如果提供 init,同一个模块会被打包到浏览器侧;模块顶层代码和顶层静态 import 需要保持浏览器可运行。

依赖示例:

TypeScript
export default { name: 'panel-tabs', dependsOn: ['tabs'], install(md, context) { // 注册 Markdown 语法。 }, init(root) { // 会在 tabs 之后执行。 },} satisfies MarkdownComponentDefinition

初始化模型

浏览器运行时会合并内置组件和项目组件,自动展开依赖关系,按依赖顺序初始化,并多轮执行直到没有待初始化节点。运行时也会监听动态插入的 DOM,自动补初始化新增的 data-vp-component 节点。

项目组件脚本按页面加载:页面实际使用了哪些项目组件,就动态导入对应的 dist/public/组件名.hash.js。如果组件声明了 dependsOn,并且依赖项也是项目组件,依赖组件脚本也会被加载。

最后更新于 2026-09-22 16:37:01 UTC+8