一个运行时加载远程 Markdown 的文档预览框架,基于 Vite + React + TypeScript。
定位类似 VitePress,但区别在于:
| VitePress | DocsView | |
|---|---|---|
| Markdown 编译时机 | 构建时(编译进 HTML) | 运行时(fetch 后渲染) |
| 文档源 | 仓库内本地 .md 文件 | 任意可访问的 HTTP/HTTPS 站点(含 CDN、对象存储、Git raw) |
| 部署产物 | 每个文档站各自打包 | 一份通用 SPA + 一行 baseUrl 配置 |
VITE_DOCSVIEW_BASE_URL 读取远程文档根 URL<baseUrl>/sidebar.json 获取导航配置<baseUrl>/<file> 加载 Markdownmarkdown-it + Shiki 在浏览器端渲染{
"version": 1,
"project": "ModelGate",
"tagline": { "zh": "多租户 LLM 网关", "en": "Multi-tenant LLM Gateway" },
"footer": {
"zh": "[ICP备案号](https://beian.miit.gov.cn/) · 公安备案号",
"en": "© 2026 ModelGate"
},
"languages": ["zh", "en"],
"defaultLanguage": "zh",
"index": {
"root": "README.md",
"file": { "zh": "zh/README.md", "en": "en/README.md" }
},
"home": {
"enabled": true,
"title": { "zh": "ModelGate", "en": "ModelGate" },
"subtitle": { "zh": "多租户 LLM 网关", "en": "Multi-tenant LLM Gateway" },
"actions": [
{ "text": { "zh": "快速开始", "en": "Get Started" }, "href": "getting-started/overview", "variant": "primary" }
],
"cards": [
{ "title": { "zh": "入门", "en": "Getting Started" }, "description": { "zh": "快速了解项目", "en": "Learn the basics" }, "href": "getting-started/overview" }
]
},
"sections": [
{
"id": "getting-started",
"title": { "zh": "入门", "en": "Getting Started" },
"order": 1,
"items": [
{
"id": "overview",
"title": { "zh": "产品概述", "en": "Overview" },
"slug": "overview",
"order": 1,
"file": { "zh": "zh/overview.md", "en": "en/overview.md" },
"description": { "zh": "...", "en": "..." }
}
]
}
]
}
字段含义见 src/types/sidebar.ts。
Fork 本仓库
编辑根目录的 .env:
VITE_DOCSVIEW_BASE_URL=https://cdn.example.com/your/docs/
VITE_DOCSVIEW_TITLE=你的文档站
VITE_DOCSVIEW_DESCRIPTION=文档站副标题
npm install && npm run build
把 dist/ 上传到 EdgeOne Pages / Cloudflare Pages / Vercel / Netlify 等任意静态托管
npm install
npm run dev
默认会加载 .env 里配置的 ModelGate 文档作为示例。换源直接改 .env。
Mermaid 使用标准代码围栏;sequenceDiagram、flowchart 等图表类型写在围栏内部:
```mermaid
sequenceDiagram
participant User as 用户
User->>App: 发起登录
```
行内公式 $E = mc^2$,块级公式:
$$
\sum_{i=1}^{n} i
$$
时序图也兼容 ```sequenceDiagram 简写,此时围栏内不需要重复首行 sequenceDiagram。
npm run builddistVITE_DOCSVIEW_BASE_URL 自动生成 dist/_redirects/llms.txt 和 /*.md 会返回 302 到文档源 CDN,其他路由回退到 /index.htmlnpm run builddistVITE_DOCSVIEW_BASE_URL 自动生成 dist/edgeone.json/llms.txt 和 /*.md 会返回 302 到文档源 CDN,其他路由重写到 /index.html容器内用 Nginx 提供静态文件服务,已内置 SPA 路由回退规则,以及 /llms.txt、/*.md 到文档源的 302 跳转。
VITE_* 变量在构建时写死进产物,因此直接修改仓库根目录的 .env 后执行 docker build 即可,无需 --build-arg 或运行时 -e(容器启动后再传 -e 不会改写前端产物)。base URL 在构建期会一并注入 nginx 配置,用于 /llms.txt、*.md 的 302 跳转。
如果部署到 https://example.com/docs/ 这种子路径,只需在 .env 里加一行:
VITE_DOCSVIEW_BASE=/docs/
vite.config.ts 会自动读取作为 Vite 的 base,src/App.tsx 的 BrowserRouter basename 也会自动跟随 import.meta.env.BASE_URL,无需改代码。注意必须以 / 开头和结尾,不能用 ./。
```yaml{4,6-8} title=".cnb.yml")$...$ 与块级公式 $$...$$::: tip / warning / danger / info、::: details 标题)sidebar.json.home 配置)src/
├── main.tsx # 入口,初始化 Shiki 后挂载 React
├── App.tsx # 路由配置
├── config/runtime.ts # 读取 VITE_DOCSVIEW_BASE_URL
├── types/sidebar.ts # sidebar.json 的 TS 类型
├── lib/
│ ├── sidebar.ts # sidebar 解析(routeMap / fileMap)
│ ├── fetcher.ts # 远程文件拉取(带缓存)
│ ├── markdown.ts # markdown-it + Shiki 配置
│ ├── diagrams.ts # Mermaid 图表异步渲染与错误降级
│ ├── links.ts # DOM 链接重写
│ ├── codeBlocks.ts # 代码块复制按钮增强
│ └── search.ts # FlexSearch 全文索引
├── hooks/
│ ├── useDocument.ts # 当前路由 -> item -> 加载文档
│ └── useToc.ts # 从渲染后 DOM 提取目录
├── store/
│ ├── theme.ts # 主题(zustand + persist)
│ └── doc.ts # sidebar 配置(zustand)
├── components/ # Layout / Header / Sidebar / HomePage / BackToTop / SearchDialog 等
└── styles/index.css # 全部样式(CSS 变量 + Markdown 渲染样式)
文档源必须允许跨域(CORS)—— 即响应头包含 Access-Control-Allow-Origin: *(或你的域名)。
CNB CDN(cdn.bring.cool)、jsDelivr、GitHub raw(带 CORS 代理)、Cloudflare R2 等默认都支持。
如需对接不支持 CORS 的源,可部署一个 Cloudflare Worker / EdgeOne Edge Function 做反代。
MIT