Skip to content

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>

主题系统

CSS 变量 (globals.css)

: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-audit skill 自动审计。


API 调用

公开 API

// 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();
}

后台 API(需 JWT)

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 参考


新增页面步骤

  1. src/pages/ 创建 tag.tsx
import { useRoute } from 'wouter';

export default function TagPage() {
  const [, params] = useRoute<{ name: string }>('/tags/:name');
  // 取数 + 渲染
}
  1. App.tsx 注册路由:
<Route path="/tags/:name" component={TagPage} />
  1. (可选)添加导航链接、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 自动失效。


UI 设计规范

项目 标准
画布基准 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)

延伸阅读

Clone this wiki locally