-
Notifications
You must be signed in to change notification settings - Fork 56
Frontend Guide
one-ea edited this page Apr 25, 2026
·
2 revisions
Vite 6 + React 19 + TypeScript 单页应用。
client/
├── src/
│ ├── components/ # 可复用组件
│ │ ├── comments/ # 评论列表 + 表单
│ │ ├── cookie-consent/ # GDPR Cookie 同意横幅
│ │ ├── post-reactions/ # 表情反应(heart/like/wow/...)
│ │ ├── series-nav/ # 系列文章导航
│ │ ├── theme-toggle/ # 双主题切换
│ │ ├── toc/ # 目录侧边栏
│ │ └── ui/ # 通用 UI 原子(按钮/卡片/...)
│ ├── pages/ # 路由页面
│ │ ├── home.tsx
│ │ ├── post.tsx
│ │ ├── archive.tsx
│ │ ├── series.tsx
│ │ ├── privacy.tsx
│ │ └── admin/ # 9 个后台子页
│ │ ├── login.tsx
│ │ ├── dashboard.tsx
│ │ ├── editor.tsx
│ │ ├── pages.tsx
│ │ ├── media.tsx
│ │ ├── comments.tsx
│ │ ├── analytics.tsx
│ │ ├── backup.tsx
│ │ └── settings.tsx
│ ├── lib/
│ │ ├── api.ts # REST 客户端封装
│ │ ├── auth.ts # JWT 存取 + 刷新
│ │ ├── utils.ts # 通用工具
│ │ └── importers/ # 6 平台导入解析器
│ │ ├── wordpress.ts
│ │ ├── ghost.ts
│ │ ├── hexo.ts
│ │ ├── hugo.ts
│ │ ├── jekyll.ts
│ │ └── halo.ts
│ ├── hooks/ # 自定义 React hooks
│ ├── App.tsx # 路由总入口 (Wouter)
│ ├── main.tsx # React 挂载点
│ └── globals.css # OKLCH 双主题 CSS 变量 + Tailwind
├── functions/ # Cloudflare Pages Functions
│ ├── api/[[path]].ts # 反向代理 /api/*
│ ├── cdn/[[path]].ts # 反向代理 /cdn/*
│ ├── rss.xml.ts # RSS 透传
│ └── _middleware.ts # 安全头注入
├── public/ # 静态资源
├── vite.config.ts
└── package.json
| 角色 | 选型 | 理由 |
|---|---|---|
| 框架 | React 19 + TypeScript | 业界标准 + 类型安全 |
| 构建 | Vite 6 | 秒级热更新 + ESM 原生 |
| 路由 | Wouter | 1.5KB,比 React Router 轻 100 倍 |
| 样式 | Tailwind CSS + CSS Variables | 原子化 + OKLCH 主题 |
| 图标 | Lucide React | 树摇友好,800+ 图标 |
| Markdown | react-markdown + remark/rehype | 插件生态丰富 |
| 数学公式 | KaTeX | 服务端渲染速度 vs MathJax 快 10 倍 |
| 代码高亮 | shiki | VSCode 同款 TextMate 语法 |
| 净化 | DOMPurify | XSS 第一道防线 |
src/App.tsx 使用 Wouter 注册路由:
import { Route, Switch } from 'wouter';
<Switch>
<Route path="/" component={HomePage} />
<Route path="/posts/:slug" component={PostPage} />
<Route path="/archive" component={ArchivePage} />
<Route path="/series/:slug" component={SeriesPage} />
<Route path="/privacy" component={PrivacyPage} />
{/* Admin */}
<Route path="/admin/login" component={LoginPage} />
<Route path="/admin/:rest*" component={AdminLayout} />
<Route component={NotFound} />
</Switch>:root {
--background: oklch(1 0 0);
--foreground: oklch(0.13 0.005 260);
--card: oklch(0.98 0.002 260);
--border: oklch(0 0 0 / 8%);
--primary: oklch(0.55 0.15 200);
}
.dark {
--background: oklch(0.13 0.005 260);
--foreground: oklch(0.93 0 0);
--card: oklch(0.18 0.005 260);
--border: oklch(1 0 0 / 8%);
}components/theme-toggle/ 使用 next-themes 风格 hook:
const { theme, setTheme } = useTheme();
setTheme(theme === 'dark' ? 'light' : 'dark');
⚠️ 任何 UI 组件改动必须双主题对照检查。可触发ui-dual-theme-auditskill 自动审计。
// src/lib/api.ts
export async function fetchPost(slug: string): Promise<Post> {
const res = await fetch(`/api/posts/${slug}`);
if (!res.ok) throw new Error(await res.text());
return res.json();
}const token = localStorage.getItem('admin_token');
await fetch('/api/admin/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify(payload),
});详细端点见 API 参考。
- 在
src/pages/创建tag.tsx:
import { useRoute } from 'wouter';
export default function TagPage() {
const [, params] = useRoute<{ name: string }>('/tags/:name');
// 取数 + 渲染
}- 在
App.tsx注册路由:
<Route path="/tags/:name" component={TagPage} />- (可选)添加导航链接、SEO meta、RSS 输出
src/lib/importers/ 6 个平台解析器全部实现 Importer 接口:
export interface Importer {
name: string;
parse(input: File | string): Promise<ImportPayload>;
}
export interface ImportPayload {
posts: NewPost[];
categories?: string[];
tags?: string[];
attachments?: { url: string; name: string }[];
}后台 /admin/dashboard 上传文件后调用对应解析器,最终统一发送 POST /api/admin/import 完成入库。
新增平台只需实现接口并在 UI 注册。
const Editor = lazy(() => import('./pages/admin/editor'));
<Suspense fallback={<Spinner />}>
<Editor />
</Suspense>所有 <img> 默认 loading="lazy" decoding="async"。
中文字体走系统栈,避免远程字体阻塞首屏:
font-family: -apple-system, BlinkMacSystemFont, 'Inter',
'Segoe UI', Roboto, 'Noto Sans SC', sans-serif;Pages 默认对静态资源加 Cache-Control: public, max-age=31536000, immutable,文件名带 hash 自动失效。
| 项目 | 标准 |
|---|---|
| 画布基准 | PC 1440×900,最小退守 1280×800 |
| 间距模数 | 偶数递增 (2/4/8/12/16/20px),不超 20px |
| 大按钮 | 36-44px 高 |
| 中文字距 | letter-spacing: 0 (禁止正向) |
| 英文大标题 | >22px 施加 -0.01em ~ -0.05em |
| 行高 | B 端 1.2-1.8 / 阅读 1.6-2.0 |
| 圆角 | rounded-md |
| 卡片 hover | -translate-y-[2px] |
| 动画曲线 | cubic-bezier(0.16, 1, 0.3, 1) |
Monolith · 边缘原生全栈博客 · MIT License · 文档以 main 分支 代码为准