vitepress-rendering-strategies
English | 简体中文
⚡ 项目状态: 积极开发中 - VitePress 项目生产可用。v0.1.0+ 开始采用语义化版本。
为 VitePress
引入跨框架组件渲染能力(当前内置 React
),在不改变 VitePress
的 SSG
架构前提下提供更细粒度的渲染与 Hydration
策略,灵感参考自 Astro
的 Islands Architecture。
- 跨框架渲染: 生产就绪的 React 集成,采用统一的 Islands Architecture,正扩展到
Solid
/Svelte
/Preact
/Angular
,共享渲染原语。 - 四种客户端指令:
client:only
、client:load
、client:visible
、ssr:only
(默认)。 - SPA 同步渲染优化:
spa:sync-render
/spa:sr
,在路由切换时同步注入关键组件的预渲染输出,消除导致视觉不稳定的异步加载间隙。 - HMR 支持: 跨框架边界的完整热模块替换,保持状态,维护开发速度。
- MPA 模式兼容: 兼容
VitePress
MPA
模式。
更完整的策略设计、动机与示例,见文档 VitePress Cross-Framework Rendering Strategy。
要求
Node.js
: ^20.19.0 或 >=22.12.0VitePress
: ^1.6.3React
/ReactDOM
(可选): ^18.2.0@vitejs/plugin-react-swc
(可选): ^3.9.0
快速开始
1. 安装依赖
pnpm add -D vitepress-rendering-strategies @vitejs/plugin-react-swc
pnpm add react react-dom
2. 配置 VitePress
在
VitePress
配置中启用React
渲染策略插件:ts// .vitepress/config.ts import { defineConfig } from 'vitepress'; import vitepressReactRenderingStrategies from 'vitepress-rendering-strategies/react'; const vitePressConfig = defineConfig({ // 你的 VitePress 配置... }); // 向 Vite 配置注入 React 渲染支持与构建期优化 vitepressReactRenderingStrategies(vitePressConfig); export default vitePressConfig;
在主题增强中安装客户端运行时:
ts// .vitepress/theme/index.ts import DefaultTheme from 'vitepress/theme'; import reactClientIntegration from 'vitepress-rendering-strategies/react/client'; import type { Theme } from 'vitepress'; const theme: Theme = { extends: DefaultTheme, async enhanceApp() { await reactClientIntegration(); } }; export default theme;
3. 使用 React 组件
编写 React 组件:
tsx// components/Landing.tsx import { useState } from 'react'; export default function Landing() { return <div>Hello World</div>; }
在
Markdown
中导入React
组件并使用渲染指令:md<script lang="react"> import Landing from '../components/Landing'; </script> <Landing ssr:only spa:sr title="Hello" />
注意事项
组件标签命名
- 必须以大写字母开头(
PascalCase
风格),例如MyComp
。 - 标签名必须与同一
.md
文件内<script lang="react">
块中的本地导入名完全一致。如果使用了别名导入(如import { Landing as HomeLanding } from '...';
),则标签必须写为<HomeLanding ... />
。 - 任何不匹配情况都会在编译时跳过,并输出一条告警。
- 必须以大写字母开头(
仅支持自闭合标签
Markdown
中的React
组件必须写成自闭合形式:<Comp ... />
。- 非自闭合形式(如
<Comp>...</Comp>
)会被跳过并输出一条告警。
位置与导入
- 组件必须在同一
Markdown
页面内的<script lang="react">
块中完成导入,未导入的组件会被忽略。 - 组件可以在
Vue
的插槽/模板中使用(例如在<template #default>...</template>
内部),也会被正确发现并转换。
- 组件必须在同一
Props 传递(一次性)
- 标签上的所有非策略属性会作为字符串
props
传递给React
组件。Vue
绑定(如:page-title="page.title"
)会先由Vue
求值为DOM
属性,再在React
渲染/水合时转发为props
。这是一次性数据传递,非响应式。 - 不要通过属性传递函数或事件处理(如
onClick
);当前不支持跨框架桥接可调用的props
/事件。
- 标签上的所有非策略属性会作为字符串
支持的指令
client:only
、client:load
、client:visible
、ssr:only
(默认)。spa:sync-render
(即spa:sr
)对client:*
默认关闭,对ssr:only
默认开启(除非显式spa:sync-render:disable
/spa:sr:disable
)。
组件编写规范(TypeScript)
// components/Landing.tsx
import { useState } from 'react';
export interface LandingProps {
title: string;
}
export default function Landing(props: LandingProps) {
const [count, setCount] = useState(0);
return (
<div>
<h2>{props.title}</h2>
<button type="button" onClick={() => setCount(c => c + 1)}>
Click
</button>
<span>{count}</span>
</div>
);
}
约束:
<script lang="react">
里仅支持静态ESM import
。初始渲染时的props
为一次性快照,非响应式双向绑定(通过父级Vue
传入的数据只用于初始化)。
ssr:only
使用 Node API 的约束
- 仅当组件在单个页面上“仅以
ssr:only
形式”渲染时,才能依赖 Node API(例如node:fs
)。若同一页面上该组件同时以任意client:*
指令使用,则不得依赖 Node API。 - 由于
Vite
可能不会把环境 API 模块纳入客户端依赖图,在ssr:only
模式读取本地文件时,请通过import.meta.dirname
解析目标路径。
import { readFileSync } from 'node:fs';
import { join } from 'pathe';
const targetPath = join(import.meta.dirname, 'local-data.json');
const data = JSON.parse(readFileSync(targetPath, 'utf-8')) as {
data: unknown;
};
渲染指令与行为
指令一览
ssr:only
(默认)- 构建期完成预渲染,仅输出静态
HTML
,不执行客户端Hydration
。 - 适合静态内容、
SEO
关键内容,最利于FCP
/LCP
/SEO
,并避免增加客户端JS
体积。
- 构建期完成预渲染,仅输出静态
client:load
- 先预渲染
HTML
,随后在客户端立即Hydration
接管交互。 - 适合首屏关键且需要交互的组件;对 TTI 有一定压力。
- 先预渲染
client:visible
- 预渲染
HTML
,组件进入视口后再Hydration
。 - 适合非首屏交互组件(评论区、图表等);脚本默认预加载,非纯惰性。
- 预渲染
client:only
- 仅客户端渲染,无 SSR/SSG 预渲染。
- 适合强宿主依赖或非关键、轻量组件。
指令速查表
指令 | 是否预渲染 HTML | 客户端 Hydration | 触发时机 | 典型场景 | spa:sr 默认 |
---|---|---|---|---|---|
ssr:only | 是 | 否 | N/A | 静态/SEO 关键内容 | 开启 |
client:load | 是 | 立即 | 预加载模块,加载后即水合 | 首屏关键交互组件 | 关闭 |
client:visible | 是 | 可见时 | 预加载;进入视口后水合 | 非首屏交互(评论、图表等) | 关闭 |
client:only | 否 | N/A | 仅客户端 | 强宿主依赖或轻量小部件 | 关闭 |
SPA 同步渲染(spa:sync-render
/ spa:sr
)
VitePress
在 SPA
路由切换时,Vue
内容同步更新;非 Vue
组件(如 React
)的预渲染 HTML
与客户端脚本加载是异步的,在弱网环境和低性能设备上容易造成 闪烁 现象。spa:sr
通过将目标页面中使用该指令的组件的预渲染输出合并到 Vue
的客户端脚本,高优阻塞下载和解析所有使用 spa:sr
指令的组件的 CSS
模块,同步渲染来消除闪烁现象。
默认规则:
client:only
组件不支持spa:sr
。- 使用
client:*
指令的组件默认不开启spa:sr
,除非显式(spa:sr
/spa:sync-render
)标注。 - 使用
ssr:only
的组件(以及无指令组件)默认开启spa:sr
,除非显式(spa:sr:disable
/spa:sync-render:disable
)标注。
权衡:spa:sr
改善切页体验,但会增大切页加载的客户端脚本体积,建议对 关键渲染组件 启用。
示例:
<Landing client:load spa:sr title="Home" />
<Hero ssr:only />
<Chart client:visible />
<Widget client:only />
包体积提示:体积增加仅发生在 SPA 切页时加载的页面客户端脚本;不影响首屏使用的 .lean.js
(VitePress 首次渲染用以水合的精简脚本)。
使用建议与限制
- 默认以
ssr:only
渲染,建议仅对需要交互的关键组件使用client:load
;非首屏交互组件用client:visible
;client:only
适合强宿主依赖或降级。 - 对开启
spa:sr
的组件保持克制,避免过多增大切页脚本负担。
与 Vue 组合使用
- 可在
Vue
组件内部通过插槽嵌入React
组件。初始渲染时,容器占位节点的属性会先被Vue
求值,再以一次性props
传递给React
组件。
<script setup>
import Parent from './Parent.vue';
</script>
<script lang="react">
import Child from './Child';
</script>
<Parent>
<template #default="{ msg }">
<Child client:only title="Hello" :message="msg" />
</template>
</Parent>
排错 / FAQ
- 标签被忽略:确保标签以大写字母开头、名称与本地导入名完全一致,且
React
标签必须自闭合。 - 组件未渲染:组件必须在同一
.md
内的<script lang="react">
完成导入,且不要放在围栏代码块中。 - 切页闪烁:对首屏关键组件启用
spa:sr
。 - 水合报错:运行时会回退到客户端渲染;请确保服务端标记与客户端输出一致,避免通过属性传递函数。
- Node API 使用报错:仅在该页面 纯
ssr:only
渲染时使用,并通过import.meta.dirname
解析路径。
本地开发与调试(DX)
为开发 vitepress-rendering-strategies
提供了优化的本地调试体验:在预览文档站点中实时加载源码改动,支持 JavaScript Debug Terminal 下的断点调试,无需手动重启服务器。
准备 docs 项目以消费本地包:
bashpnpm install pnpm build # 首次构建,产出 dist/* 供开发期构建使用 pnpm docs:dev-prepare
在 JavaScript Debug Terminal 中启动文档:
- VS Code:Terminal → New JavaScript Debug Terminal
- 执行:
bashpnpm docs:dev
该命令会等待
dist/node/index.js
可用,再启动 VitePress 开发服务器。你可以在库源码中(例如src/node/**
、src/node/react/**
)放置debugger;
,当相关代码路径被执行时,调试器会自动断点。修改、保存、继续:
- 在
vitepress-rendering-strategies
下的源码改动会触发 Vite 自动重建相关的 config 模块;无需手动重启服务器。 - 客户端/运行时代码建议用浏览器 DevTools 调试;Node/插件侧代码建议用 JavaScript Debug Terminal(或
debugger;
)。
- 在
提示:若需切回使用已构建产物,可执行:
pnpm docs:prod-prepare
参与贡献
欢迎社区贡献!更多详情请参阅 贡献指南。
许可证
MIT © XiSenao