技术栈
| 类别 | 技术 |
|---|---|
| 框架 | Nuxt 4 (Vue 3 + Nitro 2 + Vite 7) |
| 语言 | TypeScript |
| 包管理器 | pnpm |
| UI 风格 | 98.css — Windows 98 经典复古风格 |
| 内容管理 | Notion API — 用 Notion 作为无头 CMS |
| Markdown 渲染 | marked + notion-to-md |
| 字体 | Zpix(中文像素字体)、VT323(英文等宽像素字体) |
项目结构
homepage/
├── nuxt.config.ts # Nuxt 配置(兼容性日期、全局 CSS、自定义元素)
├── app/
│ ├── app.vue # 根组件
│ ├── assets/
│ │ └── css/
│ │ └── main.css # 全局样式(98.css 导入、字体、滚动条、选中色等)
│ └── pages/
│ ├── index.vue # 首页 — Win98 桌面风格主页
│ └── p/
│ └── [id].vue # 子页面 — WordPad 风格文章阅读器
├── server/
│ └── api/
│ ├── counter.ts # 访客计数器 API(读写 Notion 数据库)
│ ├── posts.ts # 文章列表 API(读取 Notion 页面子块)
│ └── page/
│ └── [id].ts # 文章详情 API(Notion 块 → Markdown 转换)
├── public/
│ ├── fonts/ # Zpix 像素字体文件
│ ├── img/ # 静态图片(头像等)
│ └── robots.txt
└── .env # 环境变量(Notion API Key、页面/数据库 ID)
核心实现
1. Win98 桌面 UI
基于 98.css 构建,完整模拟经典 Windows 98 界面:
- 窗口系统:标题栏(最小化/最大化/关闭按钮)、状态栏、凹陷面板(
sunken-panel) - 任务栏:开始按钮、窗口切换按钮、系统托盘时钟
- 开始菜单:经典竖排 Windows 98 侧边栏 + 菜单项
- 工具栏/菜单栏:模拟记事本和 WordPad 风格
- 复古元素:
<marquee>跑马灯公告、88×31 像素徽章、LED 数码管风格访客计数器 - 滚动条:WebKit 自定义滚动条模拟 Win98 经典样式
- 焦点样式:
dotted虚线框(Win98 标准) - 选中色:
#000080深蓝底白字
2. Notion 作为无头 CMS
所有内容存储在 Notion 中,通过 Notion API 实时读取:
- 文章列表(
posts.ts):读取指定 Notion 页面的子块,支持child_page和paragraph类型 - 文章详情(
page/[id].ts):读取页面块 → 将child_page块劫持改写为带链接的段落 →notion-to-md转换为 Markdown →marked渲染为 HTML - 访客计数器(
counter.ts):读取 Notion 数据库中的数字属性 → +1 → 写回,使用旧版 REST API(Notion-Version: 2022-06-28)兼容 UI 创建的数据库
3. 连接稳定性
针对 Nuxt 4 开发模式下 Vite HMR 导致的 ECONNRESET 问题,所有 Notion API 调用均包裹了 withRetry() 重试逻辑:
- 仅对网络类错误(
ECONNRESET、fetch failed)重试 - 递增延迟:500ms → 1000ms
- 业务错误(如
object_not_found、validation_error)不重试,直接抛出
4. 安全性
- Markdown 渲染时禁用原始 HTML(
marked的htmlrenderer 进行</>转义),防止 XSS - Notion API Key 通过
.env环境变量管理,不提交到版本控制
环境变量
# .env
NOTION_API_KEY=ntn_xxxx # Notion 集成密钥
NOTION_PAGE_ID=xxxx # 文章列表所在页面 ID
NOTION_COUNTER_DB_ID=xxxx # 访客计数器数据库 ID
运行
pnpm install
pnpm dev # 开发模式 → http://localhost:3000
pnpm build # 生产构建
pnpm preview # 预览生产构建