简介
因开发需要,需要在一个产库中开发多个前端 H5
项目,类似 monorepo ,这样便于项目间的管理和资源复用。
技术选型
melos
缺点:使用 melos
提供的命令去运行项目时, vscode
会失去命令行热更新(运行项目后,后续改动可按r
可热更新项目),未在其他 IDE
中尝试。且 melos
是使用 shell
执行命令,作为熟悉 JavaScript
的前端同学需增加学习成本。
pnpm
基于以上考虑,最终选择使用 pnpm
。虽然 pnpm
只是包管理工具,但它允许运行定义在 package.json
文件中的脚本,再通过参数传递即可达到想要的效果。
架构搭建
初始化 package.json
pnpm init
创建运行脚本
在根目录下创建 scripts
文件夹,在文件夹下创建 run.js
脚本。在 package.json
中的 scripts
下新增
"serve": "node scripts/run.js run"
表示执行run.js
脚本中的 run
命令。
修改 package.json
中 type
为 module
编写运行脚本
项目结构
├─lib
│ ├─project_1
│ │ ├─lib
│ │ │ └─main.dart
│ │ ├─web
│ │ └─pubspec.yaml
│ │
│ ├─project_2
│ │ ├─lib
│ │ │ └─main.dart
│ │ ├─web
│ │ └─pubspec.yaml
│ │
因 flutter run
命令无法指定 web
文件夹目录,所以每个项目都有自己的 web
目录
安装依赖
pnpm i commander -D
使用 commander 检查参数
import { program } from "commander";
program
.command("run")
.requiredOption("-p, --project <string>", "project name") // 要运行的项目名
.requiredOption( // 运行的环境
"-e, --env <string>",
"dev or prod environment",
(arg) => {
if (!["dev", "prod"].includes(arg)) {
throw new Error(
`Environment must be either 'dev' or 'prod', but got '${arg}'.`
);
}
return arg;
},
["dev", "prod"]
)
.option("--web-renderer <string>", "web renderer mode", "html") // 渲染方式
.action((cmd) => {
run(cmd);
});
program.parse(process.argv);
需要传递其他参数的,自行添加
执行对应项目
主要使用 spawn
执行 flutter run
命令,使用 readline
接受命令行输入并传递,解决 melos
缺点。
import path from "path";
import readline from "readline";
import { spawn } from "child_process";
/**
* @param {{ project: string, env: string, webRenderer: string, host: boolean }} args
*/
function run(args) {
const runPath = path.resolve(`./lib/${args.project}`);
// 使用 spawn 执行命令
const flutterProcess = spawn(
"flutter.bat",
[
"run",
"-d",
"chrome",
`--dart-define=INIT_ENV=${args.env}`, // 设置项目环境
"--web-renderer",
args.webRenderer,
],
{
cwd: runPath, // 设置命令执行路径
}
);
// 监听标准输出
flutterProcess.stdout.on("data", (data) => {
console.log(data.toString());
});
// 监听错误输出
flutterProcess.stderr.on("data", (data) => {
console.error(data.toString());
});
// 接受命令行输入并传递给 flutter run 进程
const readlineInterface = readline
.createInterface(process.stdin, process.stdout)
.on("line", (line) => {
flutterProcess.stdin.write(line);
});
flutterProcess.on("close", () => {
readlineInterface.close();
});
}
完整代码
import path from "path";
import readline from "readline";
import { spawn } from "child_process";
import { program } from "commander";
program
.command("run")
.requiredOption("-p, --project <string>", "project name") // 要运行的项目名
.requiredOption( // 运行的环境
"-e, --env <string>",
"dev or prod environment",
(arg) => {
if (!["dev", "prod"].includes(arg)) {
throw new Error(
`Environment must be either 'dev' or 'prod', but got '${arg}'.`
);
}
return arg;
},
["dev", "prod"]
)
.option("--web-renderer <string>", "web renderer mode", "html") // 渲染方式
.action((cmd) => {
run(cmd);
});
program.parse(process.argv);
/**
* @param {{ project: string, env: string, webRenderer: string, host: boolean }} args
*/
function run(args) {
const runPath = path.resolve(`./lib/${args.project}`);
// 使用 spawn 执行命令
const flutterProcess = spawn(
"flutter.bat",
[
"run",
"-d",
"chrome",
`--dart-define=INIT_ENV=${args.env}`, // 设置项目环境
"--web-renderer",
args.webRenderer,
],
{
cwd: runPath, // 设置命令执行路径
}
);
// 监听标准输出
flutterProcess.stdout.on("data", (data) => {
console.log(data.toString());
});
// 监听错误输出
flutterProcess.stderr.on("data", (data) => {
console.error(data.toString());
});
// 接受命令行输入并传递给 flutter run 进程
const readlineInterface = readline
.createInterface(process.stdin, process.stdout)
.on("line", (line) => {
flutterProcess.stdin.write(line);
});
flutterProcess.on("close", () => {
readlineInterface.close();
});
}
以后只要执行
npm run serve -- --project xxx
即可运行对应项目