N13第 1 / 1 课时未开始内容版本 1.0.0 · 核验于 2026-10-26

本地持久化与备份

本地持久化与备份 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。

学习目标

数据是个人知识站的命根子。本课给数据安家:SQLite 文件固定在 data/ 目录,配上导出、恢复和备份格式版本迁移三件套,最后用「删库重来」的狠招验证备份真的能救回记录。

问题场景

重装系统、或手滑删了 data/ 目录——三个月的笔记、进度、划线全部蒸发。又或者备份是三个月前导出的,字段结构已经对不上新代码。没有备份策略的数据等于不存在;没有版本概念的备份等于定时炸弹。

核心解释

先安家,再备份。 数据目录不能写死在代码里,用环境变量留出口:

// lib/db/client.ts(节选)
import fs from "node:fs";
import path from "node:path";
import { DatabaseSync } from "node:sqlite";

export const DATA_DIR =
  process.env.REACTLAB_DATA_DIR ?? path.join(process.cwd(), "data");
export const DB_PATH = path.join(DATA_DIR, "database", "reactlab.db");

export function getDb(): DatabaseSync {
  fs.mkdirSync(path.dirname(DB_PATH), { recursive: true });
  const db = new DatabaseSync(DB_PATH);
  db.exec("PRAGMA journal_mode = WAL;");
  return db;
}

导出:把数据库拍成一份带版本号的 JSON。

// app/api/backup/export/route.ts(核心逻辑)
import { getDb } from "@/lib/db/client";

export async function GET() {
  const db = getDb();
  const backup = {
    backupVersion: 1,                        // 格式版本,恢复时的兼容依据
    exported_at: new Date().toISOString(),
    notes: db.prepare("SELECT * FROM note").all(),
    progress: db.prepare("SELECT * FROM lesson_progress").all(),
  };
  return new Response(JSON.stringify(backup, null, 2), {
    headers: {
      "content-type": "application/json",
      "content-disposition": 'attachment; filename="reactlab-backup.json"',
    },
  });
}

恢复:合并式导入。 逐条按主键比对——库里没有就插入,有就跳过或按 updated_at 新者胜,已存在的本地记录不被旧备份覆盖。整个导入包在事务里,中途失败整体回滚,不会留下半份残局。

版本迁移。 备份格式升级时(比如 v2 给笔记加了 tags 字段),导入入口先看 backupVersion:v1 就补上默认字段再入库,而不是让 SQL 报错。数据库本身的结构升级走迁移脚本(N15 展开),两者各管一段:迁移管库结构,backupVersion 管备份文件格式。

示例拆解

真刀真枪验证「记录不丢」:

  1. 写几条笔记,执行 curl http://localhost:3000/api/backup/export -o backup.json。
  2. 打开导出文件,确认 backupVersion 与数据条数符合预期。
  3. 把 data/database/ 整个改名藏起来(模拟删库),重启应用——空库自动重建。
  4. 调用导入接口恢复 backup.json,刷新页面——笔记全部回来,created_at 保持原值。

关键点:验证备份的唯一可信方式就是「真的恢复一次」。没走过恢复流程的备份只是心理安慰。

动手任务

在本机工程里完成:

  1. 确认数据都落在 REACTLAB_DATA_DIR(默认 data/)下,把该目录加入 .gitignore。
  2. 验证或实现 export 路由,导出文件必须含 backupVersion 字段。
  3. 完整走一次「藏库 → 重建 → 导入恢复」,核对笔记数量与字段完整性。

常见错误

  • 备份只导了单张表:恢复后笔记在、进度丢;导出范围要覆盖所有业务表。
  • 导入不做事务:第 37 条失败留下前 36 条,重试又重复导入;事务保证原子性。
  • backupVersion 写在文档里而不是数据里:恢复代码拿不到版本号,迁移无从谈起。
  • 把 data/ 提交进 git:二进制库文件每次运行都产生无意义 diff,还可能泄漏私密笔记。

检查与复盘

自查清单:

  • 改 REACTLAB_DATA_DIR 换目录启动,数据目录跟着走
  • 完整走过一次删除-恢复循环,记录数与字段核对无误
  • 备份文件自描述:版本号、时间戳、数据齐全

参考资料

检查题共 2 题,提交后立即显示解析
答案由服务端判定;答错的题会自动进入复习计划。
Q1单选中等

知识站的全部用户数据都在本地单个 SQLite 数据库文件里。关于备份与恢复,正确的做法是?

Q2读代码较难

阅读两种备份方式。应用正在运行、数据库处于 WAL 模式且有持续写入。哪个说法正确?

// 方式一:不停服,直接复制主库文件
cp data/reactlab.db data/backup.db

// 方式二:用 SQLite 的快照命令
import { DatabaseSync } from 'node:sqlite';
const db = new DatabaseSync('data/reactlab.db');
db.exec("VACUUM INTO 'data/backup.db'");

读完这一课了?

标记完成后,工作台会安排接下来的学习。

Command Palette

Search for a command to run...