跳转到内容

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

唯一可提交数据源在 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:

src/bookmarks/shared/data/queries.ts
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 热更新,刷新页面即可看到新数据。

保存时 serializeBookmarkSections() 会:

  1. 按数组下标 重算 sortOrder(拖拽排序后保证连续)
  2. 去掉空 optional 字段
  3. 输出完整 TS 文件(含 interface 与 export const bookmarkSections

写文件前会备份为 bookmarks.ts.bak

  1. 打开 db/data/bookmarks.ts,在任意 card 的 bookmarks 数组追加一项

  2. 保存文件(或通过管理端保存)

  3. 访问 /bookmarks/nav/,确认新链接出现

  4. 可选:用 getBookmarkSections 在临时 Astro 页面打印调试

仓库提供 scripts/migrate-bookmarks.mjs,可从历史 MDX 书签页批量导入到 bookmarks.ts。适合从纯文档方案迁移到结构化数据。