02 · 数据模型与查询
书签数据是 三层嵌套:分区 → 卡片组 → 链接。源数据以 TypeScript 文件维护,getBookmarkSections() 按 sortOrder 排序后供页面使用。
// src/bookmarks/shared/types.ts(简化)interface BookmarkData { title: string url: string description?: string badgeText?: string badgeVariant?: string sortOrder: number}
interface BookmarkCardData { title: string sortOrder: number bookmarks: BookmarkData[]}
interface BookmarkSectionData { title: string sortOrder: number stagger: boolean // 导航页卡片是否交错布局 cards: BookmarkCardData[]}stagger 是展示层字段,随 section 一并写入 bookmarks.ts。
数据源:TypeScript 文件
Section titled “数据源:TypeScript 文件”唯一可提交数据源在 db/data/bookmarks.ts:
export const bookmarkSections: BookmarkSectionData[] = [ { title: "常用", sortOrder: 0, stagger: true, cards: [ { title: "文档", sortOrder: 0, bookmarks: [ { title: "Astro", url: "https://astro.build", sortOrder: 0 }, ], }, ], },];为什么用 TS 而不是 JSON?
- 可带类型与注释
- 管理端保存时整文件重写,格式稳定
- 直接进 Git diff,review 友好
getBookmarkSections() 直接 import bookmarkSections,按 sortOrder 排序 section / card / bookmark:
import { bookmarkSections } from '../../../../db/data/bookmarks.ts'
export async function getBookmarkSections(): Promise<BookmarkSectionData[]> { return [...bookmarkSections] .sort((a, b) => a.sortOrder - b.sortOrder) .map(section => ({ ...section, cards: [...section.cards] .sort((a, b) => a.sortOrder - b.sortOrder) .map(card => ({ ...card, bookmarks: [...card.bookmarks].sort((a, b) => a.sortOrder - b.sortOrder), })), }))}书签量级在数百条时,内存排序足够;无需数据库中间层。
改 bookmarks.ts 后 dev 会通过 Vite HMR 热更新,刷新页面即可看到新数据。
序列化(管理端写回)
Section titled “序列化(管理端写回)”保存时 serializeBookmarkSections() 会:
- 按数组下标 重算
sortOrder(拖拽排序后保证连续) - 去掉空 optional 字段
- 输出完整 TS 文件(含 interface 与
export const bookmarkSections)
写文件前会备份为 bookmarks.ts.bak。
添加第一个书签
Section titled “添加第一个书签”-
打开
db/data/bookmarks.ts,在任意 card 的bookmarks数组追加一项 -
保存文件(或通过管理端保存)
-
访问
/bookmarks/nav/,确认新链接出现 -
可选:用
getBookmarkSections在临时 Astro 页面打印调试
从旧 MDX 迁移
Section titled “从旧 MDX 迁移”仓库提供 scripts/migrate-bookmarks.mjs,可从历史 MDX 书签页批量导入到 bookmarks.ts。适合从纯文档方案迁移到结构化数据。