之前做项目时,在两个 Repo 中分别管理后端 API 和 App,在手工同步 API 定义的过程中吃了不少亏。最近刚学到 Monorepo 这种项目管理方式,于是在新项目进行尝试。以下内容由 AI 提供,修复一些小问题并验证了一遍。
基于 pnpm + Turborepo 的标准化搭建步骤。这套方案的核心在于利用 OpenAPI (Swagger) 作为中间桥梁,实现从 Next.js 到 Flutter 的自动化类型同步。
以下是从零开始新建项目的完整步骤和配置文件内容。
📂 第一步:初始化 Monorepo 根目录
首先,我们需要建立仓库的基础结构和包管理器配置。
-
创建目录并初始化 在终端执行以下命令:
mkdir my-super-app cd my-super-app git init pnpm init -
配置工作区 (
pnpm-workspace.yaml) 在根目录创建pnpm-workspace.yaml,告诉 pnpm 我们的项目包含哪些部分:packages: - "apps/*" - "packages/*" -
安装核心依赖 我们需要
turbo来管理任务管道,npm-run-all来并行执行脚本。pnpm add turbo npm-run-all -Dw注意:
-Dw表示将其作为开发依赖安装到根目录的工作区中。 -
配置构建管道 (
turbo.json) 在根目录创建turbo.json,定义构建和开发的依赖关系:{ "$schema": "https://turbo.build/schema.json", "tasks": { "build": { "dependsOn": ["^build"], "outputs": [".next/**", "!.next/cache/**", "build/**", "dist/**"] }, "dev": { "cache": false, "persistent": true }, "lint": {}, "generate:api-spec": {} } }
🚀 第二步:搭建 Next.js 后端 (API)
我们将创建一个标准的 Next.js 应用作为 API 服务器,并配置它生成 OpenAPI 规范。
-
创建应用
mkdir -p apps cd apps npx create-next-app@latest api --typescript --eslint --tailwind --app --src-dir --import-alias "@/*" # 按照提示选择 Yes cd .. -
配置 API 生成脚本 为了让 Flutter 能读取类型,我们需要 Next.js 能输出
openapi.json。- 安装依赖 (在
apps/api目录下):cd apps/api pnpm add @asteasolutions/zod-to-openapi swagger-ui-dist cd ../.. - 添加脚本 (修改
apps/api/package.json): 我们需要一个脚本来启动 API 并生成 spec 文件。{ "name": "api", "version": "0.1.0", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint", "generate:api-spec": "node ./scripts/generate-spec.js" } } - 创建生成脚本文件 (
apps/api/scripts/generate-spec.js): 这是一个简单的 Node 脚本,用于在构建时或手动触发时生成 JSON。const fs = require('fs'); const path = require('path'); // 这里仅作演示,实际项目中你需要根据 Zod Schema 生成 // 或者使用 next-swagger-doc 等库在运行时导出 const mockSpec = { "openapi": "3.0.0", "info": { "title": "My App API", "version": "1.0.0" }, "paths": {} }; const outputPath = path.join(__dirname, '../../../packages/api-spec/openapi.json'); // 确保目录存在 const dir = path.dirname(outputPath); if (!fs.existsSync(dir)){ fs.mkdirSync(dir, { recursive: true }); } fs.writeFileSync(outputPath, JSON.stringify(mockSpec, null, 2)); console.log('✅ API Spec generated at packages/api-spec/openapi.json');
- 安装依赖 (在
📱 第三步:搭建 Flutter 前端
-
创建应用 回到根目录,创建 Flutter 项目:
cd apps flutter create mobile --org com.example.myapp cd .. -
配置 Flutter 代码生成 我们需要 Flutter 能够读取
packages/api-spec中的 JSON 并生成代码。-
添加依赖 (
apps/mobile/pubspec.yaml):dev_dependencies: flutter_test: sdk: flutter build_runner: ^2.4.0 openapi_generator: ^6.0.0 # 或者使用 retrofit + json_serializable -
配置生成器 (
apps/mobile/build.yaml): 在apps/mobile目录下创建build.yaml,指定输入文件的路径(指向 Monorepo 的共享包):targets: $default: builders: openapi_generator: options: input: ../../packages/api-spec/openapi.json output: lib/api_client/ # 其他配置...
-
🔗 第四步:创建共享包与根脚本
这是 Monorepo 的“魔法”所在,我们将所有东西串联起来。
-
创建共享包目录
mkdir -p packages/api-spec # 可以在这里放一个空的 openapi.json 占位,防止首次运行报错 echo '{}' > packages/api-spec/openapi.json -
配置根目录脚本 (
package.json) 修改根目录的package.json,添加统一的管理命令:{ "name": "my-super-app", "version": "1.0.0", "private": true, "scripts": { "dev": "turbo run dev", "build": "turbo run build", "lint": "turbo run lint", "clean": "turbo run clean", "generate:api-spec": "turbo run generate:api-spec", "generate:mobile-client": "cd apps/mobile && dart run build_runner build --delete-conflicting-outputs", "sync": "pnpm run generate:api-spec && pnpm run generate:mobile-client" }, "devDependencies": { "npm-run-all": "^4.1.5", "turbo": "^2.9.1" }, "packageManager": "pnpm@8.10.0" }
🛠️ 第五步:配置 VS Code 统一开发环境
为了让 VS Code 同时完美支持 Dart 和 TypeScript,请在根目录创建 .vscode 文件夹,并添加以下文件:
-
推荐插件 (
.vscode/extensions.json){ "recommendations": [ "Dart-Code.flutter", "Dart-Code.dart-code", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "bradlc.vscode-tailwindcss" ] } -
工作区设置 (
.vscode/settings.json) 这是关键,它告诉 VS Code 如何处理多语言环境:{ "files.watcherExclude": { "**/.git/objects/**": true, "**/node_modules/**": true, "**/apps/mobile/build/**": true, "**/apps/api/.next/**": true }, "eslint.workingDirectories": [ { "mode": "auto", "path": "./apps/api" } ], "dart.openDevTools": "flutter", "[dart]": { "editor.formatOnSave": true, "editor.defaultFormatter": "Dart-Code.dart-code" }, "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true } } -
调试配置 (
.vscode/launch.json) 配置一个“一键启动全栈”的按钮:{ "version": "0.2.0", "configurations": [ { "name": "Launch Next.js API", "type": "node", "request": "launch", "cwd": "${workspaceFolder}/apps/api", "runtimeExecutable": "pnpm", "runtimeArgs": ["run", "dev"], "console": "integratedTerminal" }, { "name": "Launch Flutter Mobile", "type": "dart", "request": "launch", "cwd": "${workspaceFolder}/apps/mobile", "program": "${workspaceFolder}/apps/mobile/lib/main.dart" } ], "compounds": [ { "name": "🚀 Fullstack Dev (API + Mobile)", "configurations": ["Launch Next.js API", "Launch Flutter Mobile"], "presentation": { "hidden": false, "group": "fullstack", "order": 1 } } ] }
🎯 最终工作流
现在,你的开发流程变成了这样:
- 启动开发:在 VS Code 中按
F5选择 "🚀 Fullstack Dev",后端和模拟器会同时启动。 - 修改后端:在
apps/api中修改 API 逻辑或 Zod 定义。 - 同步类型:在终端运行
pnpm run sync。- 这会自动从 Next.js 生成最新的
openapi.json。 - 然后自动触发 Flutter 的代码生成器,更新
apps/mobile/lib/api_client中的代码。
- 这会自动从 Next.js 生成最新的
- AI 辅助:你可以直接问 AI:“我修改了 API 的用户模型,请帮我更新 Flutter 的 UI 以显示新字段”,AI 会理解整个 Monorepo 的上下文并给出准确代码。
更新:
在 Flutter 项目中添加 openapi_generator包时,会遇到了版本冲突问题,openapi_generator 已经掉队了。一个解决方式是:“Node.js 侧生成 Dart SDK”。
思路:利用 Monorepo 中的 Next.js 环境(Node.js)生成 OpenAPI 规范,并调用 CLI 工具生成 Dart 代码,将生成的 SDK 作为“包”引入 Flutter。
具体操作:
-
在 Next.js 侧安装生成器:
# 在 next.js 项目 pnpm add -D @openapitools/openapi-generator-cli -
添加生成脚本(
scripts/generate-mobile-client.js):const { execSync } = require('child_process'); // 使用 CLI 工具,完全避开 Flutter 环境的 build_runner execSync('npx openapi-generator-cli generate -i ../../packages/api-spec/openapi.json -g dart -o ../../packages/dart-client/'); -
在 Flutter 中引用:
# apps/mobile/pubspec.yaml dependencies: dart_client: path: ../../packages/dart-client -
更新
turbor.json 增加生成的 task generate:mobile-client
{ ... "tasks": { "generate:mobile-client": { "dependsOn": ["generate:api-spec"] } } }修改根目录的
package.json,添加统一的生成 SDK 的命令:{ ... "scripts": { ... "generate:mobile-client": "turbo run generate:mobile-client", "sync": "pnpm run generate:api-spec && pnpm run generate:mobile-client" } }
在终端运行 pnpm run sync。
- 这会自动从 Next.js 生成最新的
openapi.json。 - 然后自动触发 Flutter 的代码生成器,更新
packages/dart-client/中的代码。