Skip to content

框架可维护性指南

目标与边界

本指南用于定义该 VitePress 框架的可扩展工程体系。
核心原则是通过配置与注册 API 扩展功能,而不是修改系统内核代码。

详细页面

目录职责

  • 运行时与 API:.vitepress/utils/vitepress/**
  • 主题组件:.vitepress/theme/components/**
  • 多语言资源:.vitepress/config/locale/**
  • Markdown 插件实现:.vitepress/plugins/**
  • 插件注册入口:.vitepress/config/markdown-plugins.ts

内部导入必须统一使用别名(@utils@config@components),避免路径耦合。

可扩展 API(无需改系统内核)

1. 排版风格注册表

API:@utils/vitepress/api/frontmatter/hero/HeroTypographyRegistryApi

ts
import { heroTypographyRegistry } from "@utils/vitepress/api/frontmatter/hero";

heroTypographyRegistry.registerStyle({
    type: "editorial-soft",
    aliases: ["soft-editorial"],
    motion: {
        intensity: 0.9,
        title: { x: 6, y: -4, scale: 1.03 },
        text: { x: 8, y: 3, scale: 1.02 },
        tagline: { x: 4, y: 6, scale: 1.01 },
        image: { x: 5, y: -2, scale: 1.015 },
        transitionDuration: 520,
        transitionDelayStep: 36,
        transitionEasing: "cubic-bezier(0.2, 0.9, 0.2, 1)",
    },
});

随后在 frontmatter 中配置 hero.typography.type: editorial-soft 即可生效。

2. 导航下拉布局注册表

API:@utils/vitepress/api/navigation/NavDropdownLayoutRegistryApi

ts
import { navDropdownLayoutRegistry } from "@utils/vitepress/api/navigation";
import VPNavLayoutEditorial from "@components/navigation/layouts/VPNavLayoutEditorial.vue";

navDropdownLayoutRegistry.registerLayout("editorial", VPNavLayoutEditorial);

导航配置中可直接使用:

ts
dropdown: {
  layout: "editorial",
  panels: [...]
}

也可按条目指定组件覆盖:

ts
dropdown: {
  layoutComponent: "VPNavLayoutEditorial",
  panels: [...]
}

3. 浮动元素类型注册表

API:@utils/vitepress/api/frontmatter/hero/FloatingElementRegistryApi

ts
import { floatingElementRegistry } from "@utils/vitepress/api/frontmatter/hero";

floatingElementRegistry.registerType({
    type: "keyword-chip",
    renderAs: "badge",
    className: "floating-keyword-chip",
});

frontmatter 示例:

yaml
hero:
  floating:
    items:
      - type: keyword-chip
        text: Event API

若需完全自定义渲染,可直接指定组件:

yaml
hero:
  floating:
    items:
      - component: HeroFloatingCourseCard
        componentProps:
          title: KubeJS Course
          provider: GitBook

新组件开发流程

  1. .vitepress/theme/components/<category>/ 新建组件。
  2. 若组件需要被复用,导出到 .vitepress/utils/vitepress/componentRegistry/ 对应注册表。
  3. 若需在 Markdown 中直接使用,加入 .vitepress/utils/vitepress/components.ts
  4. 新增多语言 JSON:
    • .vitepress/config/locale/en-US/components/...
    • .vitepress/config/locale/zh-CN/components/...
  5. .vitepress/config/locale/component-id-mapping.json 更新组件 ID 与文件路径映射。

最小 i18n 组件模式:

vue
<script setup lang="ts">
import { useSafeI18n } from "@utils/i18n/locale";

const { t } = useSafeI18n("my-component", {
  title: "Default title",
});
</script>

<template>
  <h2>{{ t.title }}</h2>
</template>

i18n 体系说明

  • useSafeI18n 基于 VitePress 语言状态进行响应式解析。
  • 翻译缓存按 componentId@locale 维度管理。
  • 切换语言时,组件文本可无刷新更新。
  • 缺失键自动回退到代码中的默认文案。
  • 组件路径映射由 component-id-mapping.json 统一维护。

该实现避免了非响应式单例造成的语言状态滞后问题。

新增 Markdown 插件流程

  1. .vitepress/plugins/ 添加插件实现。
  2. .vitepress/config/markdown-plugins.ts 注册插件。
  3. 若插件输出依赖自定义组件,按组件流程注册全局组件。
  4. 补充中英文文档示例并验证渲染。

主题同步规范(首屏进入 + 刷新一致)

所有存在主题敏感视觉的组件(Hero 背景、主题图标、导航卡片、元信息类视觉块等)必须遵循以下规则:

  1. 不要在业务组件中直接读取 DOM 主题类名作为唯一来源。
  2. 不要只依赖原始 useData().isDark 处理首屏视觉切换。
  3. 使用 useThemeRuntime(isDark),并基于 effectiveDarkthemeReadyversion 做主题分支,保证首次进入、刷新与运行时切换都一致。
  4. Hero 子组件中统一使用 useHeroTheme(),优先读取 isDarkRef.valueresolveThemeValue(...)
  5. 对首屏敏感的 Hero 视觉层,必须通过 themeReady 控制渲染,避免 light/dark 闪烁。
  6. 禁止自动从 dark 回退到 light,或从 light 回退到 dark。共享解析器必须遵循 dark ?? valuelight ?? value
  7. 组件目录只保留视图渲染。若主题同步需要 observer、调度或共享生命周期,必须移动到 .vitepress/utils/vitepress/runtime/theme/**

相关 API:

  • @utils/vitepress/runtime/theme/useThemeRuntime
  • @utils/vitepress/runtime/theme/heroThemeContext
  • @utils/vitepress/runtime/theme/themeValueResolver

最小示例:

ts
import { useData } from "vitepress";
import { useThemeRuntime } from "@utils/vitepress/runtime/theme";

const { isDark } = useData();
const { effectiveDark, themeReady, version } = useThemeRuntime(isDark);

尺寸监听规范(Resize)

所有尺寸敏感组件必须使用共享的尺寸监听运行时,禁止重复手写 ResizeObserver 生命周期。

  1. 使用 createElementResizeState(targetRef, onResize, { debounceMs })
  2. 目标元素挂载后执行 reobserve(targetRef.value)
  3. 除非有特殊能力缺口,否则禁止在组件内部单独 new ResizeObserver
  4. 清理逻辑由共享运行时负责,组件仅保留业务更新函数。

相关 API:

  • @utils/vitepress/runtime/viewport/elementResizeState

最小示例:

ts
import { createElementResizeState } from "@utils/vitepress/runtime/viewport";

const targetRef = ref<HTMLElement | null>(null);
const { reobserve } = createElementResizeState(
  targetRef,
  () => syncLayout(),
  { debounceMs: 80 },
);

日常开发流程

修改框架能力时,建议按以下顺序推进,避免契约与实现脱节:

  1. 先改契约层。 在 .vitepress/utils/vitepress/api/** 更新类型、schema、规范化逻辑。
  2. 再改共享运行时。 主题同步、尺寸监听、Hero 自适应、DOM 观察等状态逻辑统一放到 .vitepress/utils/vitepress/runtime/**
  3. 最后改视图组件。 .vitepress/theme/components/** 只负责渲染与轻量组合,不承担复杂生命周期。
  4. 同步更新示例与文档。 新 frontmatter 键或扩展点必须至少有一个 markdown 示例,并写明用法。
  5. 合并前执行对应校验命令。

推荐在仓库根目录执行:

bash
yarn locale
yarn sidebar
yarn tags
yarn build

若修改了配置或 frontmatter 契约,还应执行:

bash
yarn sync-config
yarn frontmatter

代码放置规则

以下目录是主要职责边界:

  • .vitepress/config/project-config.ts 站点级产品配置、功能开关、语言列表、搜索提供方、部署、社交链接。
  • .vitepress/config/lang/** 导航、主题、搜索等多语言配置。
  • .vitepress/config/shaders/** 内置 shader 模板与 shader 注册表。
  • .vitepress/config/markdown-plugins.ts Markdown 插件组合入口与注册顺序。
  • .vitepress/plugins/** markdown-it 插件实现。
  • .vitepress/theme/components/** Vue 视图层,只消费规范化后的配置与运行时状态。
  • .vitepress/theme/styles/** 全局样式层、变量、插件皮肤、共享组件样式。
  • .vitepress/utils/vitepress/api/** 契约类型、规范化逻辑、注册表、扩展 API。
  • .vitepress/utils/vitepress/runtime/** 有状态域:主题同步、尺寸同步、Hero 行为、媒体观察等。
  • .vitepress/utils/vitepress/componentRegistry/** 可复用组件的统一导出入口。
  • .vitepress/utils/vitepress/components.ts markdown / 运行时全局组件注册。

判断原则:

  • 负责解析、规范化、校验配置的,放 api
  • 负责生命周期、DOM 协调、观察器的,放 runtime
  • 只负责展示的,放 theme/components

运行时与函数扩展规范

新增函数、composable、service、controller 时:

  1. 纯函数与契约辅助逻辑放在 api,不要塞进组件。
  2. 有状态控制器放在 runtime,生命周期复杂时优先采用小粒度类式 API。
  3. 新增公共能力要从最近的 index.ts barrel 导出。
  4. 共享或时序敏感逻辑不要在多个组件里重复直接读取 DOM。
  5. 优先做一个共享运行时,而不是在多个组件里重复 new MutationObserver / ResizeObserver

当前可参考的成熟模式:

  • 主题稳定化:.vitepress/utils/vitepress/runtime/theme/**
  • 元素尺寸运行时:.vitepress/utils/vitepress/runtime/viewport/**
  • Hero 导航自适应:.vitepress/utils/vitepress/runtime/hero/navAdaptiveState.ts

组件与全局注册规范

新增 Vue 组件时:

  1. 放到 .vitepress/theme/components/<category>/ 的正确分类目录。
  2. 若需要被框架代码复用,先导出到 .vitepress/utils/vitepress/componentRegistry/** 对应 barrel。
  3. 若需要在 Markdown 中直接使用,再注册到 .vitepress/utils/vitepress/components.ts
  4. 若组件带文案,补齐多语言资源并同步组件 ID 映射。

面向 Markdown 的组件注册链建议固定为:

组件文件 -> componentRegistry barrel -> components.ts -> markdown / 运行时消费

配置扩展规范

新增配置或 frontmatter 字段时:

  1. 先在 .vitepress/utils/vitepress/api/frontmatter/hero/HeroFrontmatterApi.ts 或对应 API 模块补充类型。
  2. 在同一层完成旧格式与新格式的规范化。
  3. 视图组件只读取规范化后的结果,不自行兼容多种输入形态。
  4. 若字段影响导航、搜索、主题行为,应更新对应 runtime controller,而不是在组件内部重复处理。
  5. 同步补充文档页与示例页面。

站点级配置变更时:

  1. 更新 .vitepress/config/project-config.ts
  2. 若涉及标签或搜索 locale,同步更新 .vitepress/config/lang/**
  3. 若配置需要向文档元数据或生成内容传播,执行 yarn sync-configyarn frontmatter

样式扩展规范

全局样式层有明确顺序,遵循 .vitepress/theme/styles/index.css 的导入层级:

  1. 配置变量
  2. 基础样式
  3. 插件样式
  4. 共享组件样式

不同样式需求使用不同载体:

  • 组件内 scoped <style>: 只处理组件局部布局与外观。
  • .vitepress/theme/styles/** 全局 CSS: 处理跨组件 token、插件皮肤、布局原语、全站级选择器。
  • frontmatter / config 驱动的 CSS 变量: 处理运行时主题值,尤其是 hero/background/nav/search 颜色。

若一个问题可以通过 CSS 变量契约或 scoped 样式解决,就不要新增全局 ad-hoc 选择器。

Hero 扩展手册

Hero 扩展必须从契约层开始,不要直接从视图组件硬改。

1. 新增 Typography 样式

通过排版注册表挂载新样式:

ts
import { heroTypographyRegistry } from "@utils/vitepress/api/frontmatter/hero";

heroTypographyRegistry.registerStyle({
  type: "editorial-soft",
  aliases: ["soft-editorial"],
  motion: {
    intensity: 0.9,
    title: { x: 6, y: -4, scale: 1.03 },
    text: { x: 8, y: 3, scale: 1.02 },
    tagline: { x: 4, y: 6, scale: 1.01 },
    image: { x: 5, y: -2, scale: 1.015 },
    transitionDuration: 520,
    transitionDelayStep: 36,
    transitionEasing: "cubic-bezier(0.2, 0.9, 0.2, 1)",
  },
});

前端通过 hero.typography.type 使用该样式。

2. 新增 Floating Element 类型

通过浮动元素注册表扩展:

ts
import { floatingElementRegistry } from "@utils/vitepress/api/frontmatter/hero";

floatingElementRegistry.registerType({
  type: "keyword-chip",
  renderAs: "badge",
  className: "floating-keyword-chip",
});

若需要完全自定义渲染,应绑定组件,并明确文档里的 componentProps 契约。

3. 新增 Shader 模板

Shader 模板在 .vitepress/config/shaders/index.ts 注册,模板结构定义在 .vitepress/config/shaders/templates/base-shader.ts

最小写法:

ts
import { registerShaderTemplate } from "@config/shaders";
import { baseVertexShader, buildTemplate } from "@config/shaders/templates/base-shader";

registerShaderTemplate("aurora", buildTemplate({
  key: "aurora",
  vertex: baseVertexShader,
  fragment: `...`,
  defaultUniforms: {
    uIntensity: 0.8,
  },
}));

如果要作为内置预设发布,除了动态注册,还应新增独立文件并加入默认 shader registry。

4. 新增 Background Renderer 类型

如果不是新增 shader 预设,而是新增真正的背景类型:

  1. HeroFrontmatterApi.ts 中扩展 HeroBackgroundType 与相关契约。
  2. 在同一 API 层完成规范化。
  3. .vitepress/theme/components/hero/background/ 新建渲染组件。
  4. .vitepress/theme/components/hero/background/BackgroundLayer.vue 中接入类型到组件映射。
  5. 补充对应中英文示例页面。

5. 扩展 Hero Nav/Search 视觉

首页顶部导航与搜索框视觉由 hero.colors.* 契约驱动,并在 .vitepress/utils/vitepress/runtime/hero/navAdaptiveState.ts 中解析。

若需要新增 Hero 驱动的 nav/search 样式能力:

  1. 先在 HeroFrontmatterApi.ts 增加 typed color key。
  2. 再在 navAdaptiveState.ts 中消费并映射为 CSS 变量。
  3. 避免在组件内直接写主题分支,优先复用变量契约。

文档与校验清单

每个面向框架的扩展至少应同时交付:

  1. 类型更新
  2. runtime / component 集成
  3. 至少一个 markdown 示例
  4. 中英文文档同步
  5. PR 或 handoff 中记录执行过的验证命令

最低验证要求:

  • yarn locale
  • yarn sidebar
  • yarn tags
  • yarn build

维护规范

  • 非简单状态逻辑必须采用小粒度、可组合的类/API 结构。
  • 禁止新增旧路径兼容 re-export 文件。
  • 内部代码统一使用 @ 别名导入。
  • 所有扩展入口必须通过注册机制暴露。
  • 至少执行以下校验:
    • pnpm -s tsc --noEmit