基于 Cloudflare Pages + D1 数据库,为 MoonTV 影视站打造多端同步与数据持久化
1. 为什么需要多端同步与数据持久化作为一个影视爱好者我经常遇到这样的烦恼在手机上看到一半的剧集换到电脑上却找不到播放记录了辛苦收藏的片单换个设备就消失得无影无踪。MoonTV 这个开源项目本身已经做得很棒但默认的本地存储方案确实存在这个痛点。实测下来浏览器本地存储localStorage有三大硬伤一是容量有限存不了太多播放记录二是无法跨设备同步换个手机就得重新开始三是数据容易被清理一不小心就全没了。这时候 Cloudflare D1 数据库的优势就体现出来了——它是个轻量级的 Serverless SQLite 解决方案完全免费 tier 就够用还能自动同步到全球边缘节点。2. 快速上手 Cloudflare Pages 部署第一次接触 Cloudflare Pages 的朋友可能会觉得陌生其实它比传统服务器简单多了。我去年帮朋友部署过十几个类似项目总结下来最稳的流程是这样的首先 fork 官方仓库到自己的 GitHub 账号建议定期用 Sync fork 同步更新登录 Cloudflare 控制台进入 Pages 页面选择 Connect to Git 关联你的 GitHub 仓库关键配置参数要注意构建命令: pnpm install --frozen-lockfile pnpm run pages:build 输出目录: .vercel/output/static部署完成后别急着关页面还有两个重要设置在「设置」→「函数」里开启 nodejs_compat 兼容模式在「环境变量」添加 PASSWORD 变量随便设个复杂密码提示如果遇到部署失败90%的情况是没设置 nodejs_compat 或者密码变量3. D1 数据库配置实战Cloudflare 的控制台最近改版过新用户可能找不到 D1 入口。现在正确的路径是Dashboard → Workers Pages → D1 Databases。创建时建议选离你用户近的区域比如亚洲用户选 APAC。初始化数据库需要执行建表 SQLMoonTV 项目里通常会有个d1-init.sql文件。我建议直接通过控制台的 SQL 编辑器执行比命令行更直观。核心表结构包括users用户账号watch_history播放记录favorites收藏列表CREATE TABLE IF NOT EXISTS watch_history ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL, video_id TEXT NOT NULL, progress INTEGER DEFAULT 0, last_watched TIMESTAMP DEFAULT CURRENT_TIMESTAMP );环境变量配置有个小技巧除了必填的 NEXT_PUBLIC_STORAGE_TYPEd1建议把 ADMIN_USERNAME 和 ADMIN_PASSWORD 也提前设置好这样部署完就能直接进 /admin 后台。4. 深度优化与故障排查用上 D1 之后我发现播放记录同步偶尔会有 1-2 秒延迟。经过抓包分析这是边缘节点同步的固有特性。解决方案是在前端加个 loading 状态实测用户体验反而更好。常见问题及解决方法数据库连接失败检查 D1 绑定是否正确新版需要修改 wrangler.toml跨域问题在 Pages 设置的「函数」里添加 CORS 头数据不同步可能是浏览器缓存作祟试试强制刷新CtrlF5性能优化方面我推荐三个技巧为频繁查询的字段如 user_id创建索引批量写入时使用事务BEGIN...COMMIT设置合理的缓存策略减少重复查询5. 移动端适配与 PWA 增强MoonTV 默认的 PWA 配置已经很完善但结合 D1 后可以做得更好。我在 manifest.json 里增加了这些配置background_color: #0f172a, display: standalone, orientation: any实测发现 iOS 上有个坑Safari 对 Service Worker 的支持比较特殊需要在页面的 head 里额外添加meta nameapple-mobile-web-app-capable contentyes播放记录同步策略我改成了双保险机制本地存一份最新记录同时异步同步到云端。这样即使网络不稳定用户也不会感知到异常。核心代码逻辑是这样的async function saveProgress(videoId, progress) { localStorage.setItem(progress_${videoId}, progress); try { await db.prepare(INSERT OR REPLACE INTO watch_history (...) VALUES (...)) .run(...); } catch (e) { console.error(云端同步失败已降级到本地存储); } }6. 安全防护与成本控制虽然 Cloudflare 免费额度很大方但还是要预防意外情况。我在 D1 数据库设置了每日写入限额通过 Worker 脚本实现export default { async fetch(request, env) { const today new Date().toISOString().split(T)[0]; const count await env.DB.prepare( SELECT count(*) as cnt FROM audit_log WHERE date? ).bind(today).first(); if (count 10000) { return new Response(每日限额已用完, { status: 429 }); } // 正常处理请求... } }对于敏感操作如删除收藏我建议在前端和后端都做二次确认。D1 的备份功能很实用记得在控制台设置自动每日备份关键时刻能救命。