项目架构
Pivot 采用现代化的组件库架构,深受 shadcn/ui 启发,但专为 OpenAPI 规范优化。本文档将详细介绍项目的设计理念、目录结构和开发规范。
🏗️ 总体架构
设计原则
- 组件分离:明确区分可分发组件和站点专用组件
- 原子化设计:构建可组合的小组件,灵活组合成复杂界面
- 类型安全:完整的 TypeScript 支持,确保开发时的类型安全
- 按需引入:只安装需要的组件,减少包体积
- 源码透明:所有组件源码可见,方便定制和学习
架构层次
┌─────────────────────────────────────────┐
│ 用户界面 │
├─────────────────────────────────────────┤
│ Registry 组件库 │
│ ┌─────────────┬──────────────────────┐ │
│ │ pivot/ │ example/ │ │
│ │ (89+ 组件) │ (复杂示例) │ │
│ └─────────────┴──────────────────────┘ │
├─────────────────────────────────────────┤
│ 工具层 (lib/) │
│ ┌─────────┬──────────┬──────────────┐ │
│ │ hooks/ │ utils/ │ i18n/ │ │
│ └─────────┴──────────┴──────────────┘ │
├─────────────────────────────────────────┤
│ 基础设施 (Next.js) │
└─────────────────────────────────────────┘
📁 目录结构详解
可分发组件 (registry/
)
这是项目的核心,包含所有可以对外分发的组件和工具:
registry/
├── pivot/ # 89+ 原子组件
│ ├── status-code.tsx # HTTP 状态码组件
│ ├── method-label.tsx # HTTP 方法标签
│ ├── schema-display.tsx # Schema 展示组件
│ ├── try-it-out-panel.tsx # API 测试面板
│ └── ... # 更多组件
├── lib/ # 工具库
│ ├── hooks/ # React Hooks
│ │ ├── use-openapi.ts # 主要的 OpenAPI 处理 Hook
│ │ ├── use-operation.ts # 操作处理 Hook
│ │ └── use-schema.ts # Schema 处理 Hook
│ ├── utils/ # 工具函数
│ │ ├── resolve-ref.ts # 引用解析
│ │ ├── generate-example.ts # 示例生成
│ │ └── schema-utils.ts # Schema 工具函数
│ └── i18n/ # 国际化系统
│ ├── I18nProvider.tsx # 国际化 Provider
│ └── locales/ # 语言包
└── example/ # 复杂示例组件
└── operation-list-layout-demo.tsx
文档站点 (components/
, app/
, content/
)
专用于文档站点的组件和内容:
components/ # 站点专用组件(不分发)
├── ui/ # shadcn/ui 基础组件
├── magicui/ # 特效组件
├── site-header.tsx # 站点头部
├── sidebar-nav.tsx # 文档侧边栏
└── component-preview.tsx # 组件预览
content/docs/ # MDX 文档内容
├── index.mdx # 主文档页面
├── components/ # 组件文档
├── installation/ # 安装指南
└── templates/ # 模板文档
app/ # Next.js App Router
├── (docs)/ # 文档路由组
├── viewer/ # OpenAPI 查看器
└── page.tsx # 主页
🔄 代码复用策略
严格的分层原则
Pivot 采用严格的分层架构,确保代码的清晰性和可维护性:
Registry 组件层 (registry/pivot/
)
只能使用:
../lib/
下的工具和 hooks- 第三方库
- 自身定义的类型和函数
禁止使用:
@/hooks/
下的站点专用 hooks@/components/
下的站点组件
// ✅ 正确的导入
import { useOpenApi } from "../lib/hooks/use-openapi";
import { resolveRef } from "../lib/utils/resolve-ref";
import { cn } from "@/lib/utils";
// ❌ 错误的导入
import { useConfig } from "@/hooks/use-config"; // 站点专用 hook
站点组件层 (components/
)
只能使用:
@/hooks/
和@/lib/
下的站点工具@/components/ui/
下的基础组件- Registry 组件(作为依赖)
禁止使用:
- 直接导入
registry/lib/
下的工具
// ✅ 正确的导入
import { useConfig } from "@/hooks/use-config";
import { StatusCode } from "@/components/pivot/status-code";
// ❌ 错误的导入
import { useOpenApi } from "@/hooks/use-openapi";
工具函数分布
Registry 工具 (registry/lib/
)
专为 OpenAPI 处理设计,可分发:
// OpenAPI 专用工具
export function resolveRef(obj, components, category) {
// 处理 $ref 引用的专业逻辑
}
export function generateExample(schema, components, options) {
// 根据 Schema 生成示例数据
}
站点工具 (lib/
, hooks/
)
站点专用工具,不分发:
// 站点配置
export function useConfig() {
// 站点配置逻辑
}
// 文档相关工具
export function getTableOfContents(content) {
// 提取文档目录
}
🎨 样式和主题系统
Tailwind CSS 配置
基于 Tailwind CSS v4,支持暗色模式:
module.exports = {
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./registry/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
// 状态码颜色
status: {
info: "#3b82f6",
success: "#10b981",
redirect: "#f59e0b",
client: "#f97316",
server: "#ef4444",
},
},
},
},
};
组件变体系统
使用 class-variance-authority
管理组件变体:
const statusCodeVariants = cva(
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold",
{
variants: {
variant: {
default: "border-transparent text-primary-foreground",
outline: "text-foreground",
},
size: {
sm: "px-2 py-0.5 text-xs",
md: "px-2.5 py-0.5 text-xs",
lg: "px-3 py-1 text-sm",
},
},
defaultVariants: {
variant: "default",
size: "md",
},
},
);
🎣 Hooks 设计模式
useOpenApi - 核心 Hook
提供 OpenAPI 数据处理的完整功能:
export function useOpenApi(spec: OpenAPIV3.Document | null) {
// 缓存组件
const components = spec?.components;
// 优化的引用解析
const resolve = useMemo(() => {
return function resolve<T>(
obj: T | OpenAPIV3.ReferenceObject,
category?: string,
): T | null {
return resolveRef<T>(obj, components, category);
};
}, [components, spec]);
// 返回完整的 API
return {
spec,
components,
resolve,
getSchemaType,
getOperationsByTag,
// ... 更多方法
};
}
Hook 组合模式
鼓励 hooks 之间的组合使用:
function ApiEndpoint({ spec, path, method }) {
// 主 Hook
const openApi = useOpenApi(spec);
// 专用 Hook
const operation = useOperation(
openApi.getOperation(path, method),
path,
method,
);
// 模式 Hook
const requestSchema = useSchema(
operation.getRequestBody()?.content?.["application/json"]?.schema,
openApi.getComponents(),
);
return <div>{/* 使用组合的数据 */}</div>;
}
🌍 国际化架构
分布式翻译系统
export const I18nProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const [locale, setLocale] = useState<"en" | "zh">("en");
const t = (key: string): string => {
return locales[locale]?.[key] || key;
};
return (
<I18nContext.Provider value={{ locale, setLocale, t }}>
{children}
</I18nContext.Provider>
);
};
无 Provider 支持
Hook 设计为即使没有 Provider 也能工作:
export const useI18n = (): I18nContextProps => {
const context = useContext(I18nContext);
// 没有 provider 时的降级策略
if (!context) {
return {
locale: "en",
setLocale: () =>
console.warn("useI18n: setLocale called outside of provider"),
t: createDefaultTranslator("en"),
};
}
return context;
};
📦 Registry 系统
组件注册
每个组件都在 registry.json
中注册:
{
"name": "status-code",
"type": "registry:ui",
"dependencies": ["react", "class-variance-authority"],
"files": [
{
"path": "registry/pivot/status-code.tsx",
"type": "registry:ui"
}
]
}
依赖管理
支持组件间的依赖关系:
{
"name": "parameter-item",
"registryDependencies": ["status-code", "type-indicator", "required-badge"]
}
🚀 性能优化策略
1. 代码分割
// 懒加载重型组件
const TryItOutPanel = dynamic(
() => import("@/components/pivot/try-it-out-panel"),
{ loading: () => <div>Loading...</div> },
);
2. 记忆化缓存
// 缓存复杂计算
const operationsByTag = useMemo(() => {
return openApi.getOperationsByTag();
}, [openApi]);
3. 引用解析优化
// 避免重复解析
const resolveRef = useMemo(() => {
const cache = new Map();
return function resolve(obj, category) {
const key = `${obj.$ref}-${category}`;
if (cache.has(key)) return cache.get(key);
const result = resolveRefImpl(obj, components, category);
cache.set(key, result);
return result;
};
}, [components]);
🔍 质量保证
TypeScript 严格模式
{
"compilerOptions": {
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"exactOptionalPropertyTypes": true
}
}
组件 Props 验证
interface StatusCodeProps extends React.HTMLAttributes<HTMLDivElement> {
code: number;
variant?: "default" | "outline";
size?: "sm" | "md" | "lg";
}
// 运行时验证(开发模式)
if (process.env.NODE_ENV === "development") {
if (code < 100 || code > 599) {
console.warn(`Invalid HTTP status code: ${code}`);
}
}
错误边界
export function ErrorBoundary({ children, fallback }) {
return (
<Suspense fallback={<div>Loading...</div>}>
<ErrorBoundaryImpl fallback={fallback}>{children}</ErrorBoundaryImpl>
</Suspense>
);
}
📚 开发工作流
1. 组件开发
# 创建新组件
touch registry/pivot/new-component.tsx
# 添加到 registry
echo '{"name": "new-component", ...}' >> registry.json
# 构建 registry
npm run build:registry
2. 文档编写
# 创建组件文档
touch content/docs/components/new-component.mdx
# 添加示例
touch registry/example/new-component-demo.tsx
3. 测试验证
# 类型检查
npm run type-check
# 构建验证
npm run build
# 组件测试
npm run test:components
这个架构确保了 Pivot 的可扩展性、可维护性和开发者体验。通过清晰的分层和严格的规范,我们能够提供一个既强大又易于使用的 OpenAPI 组件库。