开发 W3Cay 项目时,我需要实现:生产环境使用 Cloudflare D1 数据库,本地开发使用 SQLite。这涉及 runtime 兼容性、构建配置、数据库连接管理等挑战。
技术选型
项目背景
W3Cay 是一个导航站项目,技术栈如下:
- 前端框架:Next.js 15
- 部署平台:Cloudflare Pages
- 数据库:Cloudflare D1(生产)
- 本地开发:SQLite
为什么没有用 Supabase
那为什么没有用 Supabase呢,主要是感觉 D1 和 CF 平台绑定更方便,而且给的量够多,基本用不完,我怕网站流量上来,Supabase 的免费额度顶不住,以下是一些对比
D1 vs Supabase
| D1 | Supabase | |
|---|---|---|
| 延迟 | 与 Pages 同网络,零延迟 | 需回传中心数据库,有网络跳转 |
| 免费额度 | 25,000 读/天,5GB 存储 | 500MB 存储,500MB 传输 |
| 超出费用 | $0.001/千次读取 | $25/月起 |
| 部署 | 与 Cloudflare 集成 | 独立部署 |
本地开发为什么不用 D1?
wrangler pages dev 的开发体验差:
- 无热更新:修改后需重新构建(
npm run cf:build && npm run cf:preview) - 调试困难:Edge Runtime 断点不如 Node.js 完善
- 流程繁琐:每次修改都要构建 → 预览 → 访问
如果使用本地 next dev + sqlite 就非常方便,基本上实时热更新看数据
那么下面我将总结下我如何做适配的
核心挑战
- Edge Runtime:运行在 V8 Isolate,不支持 Node.js API
- Node.js Runtime:支持完整 Node.js 生态
better-sqlite3 需要 Node.js 原生模块,只能在 Node.js Runtime 运行
而且 Next 项目中的服务渲染路由、API 都要声明 edge runtime 才能编译通过
不然会报错:
⚡️ Please make sure that all your non-static routes export the following edge runtime route segment config:
⚡️ export const runtime = 'edge';
这就是矛盾点,本地sqlite是基于 node 的本地读写API 实现的,如果使用 edge 环境,就使用不了,但 cloudflare 的编译必须要声明 edge
解决方案
管理 Edge Runtime 声明
首先解决本地的环境声明问题,默认本地使用node环境,编译 build 时使用 Edge Runtime
开发场景
新增运行环境注释标记 @CF_EDGE_RUNTIME
首先将代码中的 edge 声明都注释掉,通过 // @CF_EDGE_RUNTIME 来注释,这样给这一行一个标记,方便扫描处理,全部注释掉以后,这样我们本地开发就能直接使用sqlite了,不会出现报错
// src/app/api/random/route.ts
import { NextResponse } from 'next/server'
import { getDb, sites } from '@/db'
// @CF_EDGE_RUNTIME export const runtime = 'edge'
export async function GET() {
const db = getDb()
// ...
}
编译build场景
- 新增需要处理的页面路由或者api 清单文件,这个作为脚本处理的一个入口
// .edge-runtime-files.json
[
"src/app/[locale]/page.js",
"src/app/[locale]/site/[slug]/page.js",
"src/app/api/random/route.js",
"src/app/api/site/[abbr]/route.js"
]
- 新增构建脚本,新增两个脚本,作为编译前和编译后的钩子脚本
{
"scripts": {
"precf:build": "node scripts/manage-edge-runtime.js uncomment",
"cf:build": "npm run content:full && npx @cloudflare/next-on-pages",
"postcf:build": "node scripts/manage-edge-runtime.js comment"
}
}
具体脚本代码我就不贴了,这个让 AI 直接写一个就行
precf:build:取消注释 Edge Runtime 声明postcf:build:注释掉 Edge Runtime 声明
- sqlite3 动态导入问题
因为 sqlite3 依赖 node的 fs 等本地文件 api,如果直接 import better-sqlite3 等库,会导致开发时候就报错
使用 /* webpackIgnore: true */ 避免打包时分析模块,运行时动态加载:
const betterSqlite3Module = await import(/* webpackIgnore: true */ 'better-sqlite3')
const drizzleSqliteModule = await import(/* webpackIgnore: true */ 'drizzle-orm/better-sqlite3')
总结
实现效果
开发环境,本地 SQLite,零配置,热更新;编译时将 node 环境切换到 edge 环境,编译完成则恢复,生产上使用 CF 的 D1 数据库
关键要点
- Edge Runtime 管理:本地 Node,部署 Edge
- 动态导入:
/* webpackIgnore: true */避免打包问题
发表评论
暂无评论。成为第一个分享想法的人吧!


