umi dev 和 build 规则, 什么样的东西会进入umi.js(携带相当一部分 esnext 的 polyfill 和框架本身的运行时)
当我们运行umi dev
的时候, 一般会有个bin
文件,
我们去packages/umi/package.json
看下这个,根据入口就能找到umi dev
执行的全过程。umi
中的command
都是依赖于api.registerCommand
来实现的
bin入口文件umi.js
packages/umi/package.json
, 根据文件入口会找到bin/umi.js
packages/umi/bin/umi.js
,
在这段代码中, 上面这代码如果是debugger模式的话,当输出日志、警告或错误信息时,同时输出这些方法的代码的堆栈信息,以便于调试和定位问题。 下面那代码没啥好说,直接require
编译后dist
文件夹下的 cli.js
umi 是用father打包,所以运行father dev 或 father build的时候都会生成dist 目录
cli入口文件,对各种命令进行不同的处理
packages/umi/src/cli/cli.ts
如果是dev
命令,则运行dev()
方法, 否则Serive
对象的run2()
方法, 然后去看下dev()
, 当然我不是很清楚为什么只有dev
才需要单独写方法处理, 看看dev()
干了什么
创建子进程并处子进程的终止信号
packages/umi/src/cli/dev.ts
// 这段代码是一个Node.js函数,用于创建一个子进程并处理子进程的终止信号。
export function dev() {
// 使用`fork`方法创建一个子进程,脚本路径是`../../bin/forkedDev`.
const child = fork({
scriptPath: require.resolve('../../bin/forkedDev'),
});
// ref:
// http://nodejs.cn/api/process/signal_events.html
// https://lisk.io/blog/development/why-we-stopped-using-npm-start-child-processes
// 监听`process`对象的`SIGINT`和`SIGTERM`事件
process.on('SIGINT', () => {
child.kill('SIGINT');
// ref:
// https://github.com/umijs/umi/issues/6009
process.exit(0);
});
process.on('SIGTERM', () => {
child.kill('SIGTERM');
process.exit(1);
});
}
SIGINT
, 代表着中断信号,通常是用户按下Ctrl+C发送的信号,结束当前进程,退出码是0SIGTERM
, 代表着终止信号, 结束当前进程,退出码是1
更多信号事件
代码里都是会结束子进程,然后结束当前进程,为什么其他命令不需要这么处理? 我们根据路径../../bin/forkedDev
去看下一步骤
处理服务运行中各种终止信号
packages/umi/src/cli/forkedDev.ts
前面解析了命令行参数,创建Service实例调用run2()
方法,这不和packages/umi/src/cli/cli.ts
对上了,后面的也是监听了process
对象的SIGINT
(中断信号)、SIGQUIT
(退出信号)和SIGTERM
(终止信号)事件
(async () => {
try {
const args = yParser(process.argv.slice(2));
const service = new Service();
await service.run2({
name: DEV_COMMAND,
args,
});
let closed = false;
// kill(2) Ctrl-C 中断信号
process.once('SIGINT', () => onSignal('SIGINT'));
// kill(3) Ctrl-\ 退出信号
process.once('SIGQUIT', () => onSignal('SIGQUIT'));
// kill(15) default 终止信号
process.once('SIGTERM', () => onSignal('SIGTERM'));
function onSignal(signal: string) {
if (closed) return;
closed = true;
// 退出时触发插件中的 onExit 事件
service.applyPlugins({
key: 'onExit',
args: {
signal,
},
});
process.exit(0);
}
} catch (e: any) {
// 捕捉错误信息,记录日志
logger.fatal(e);
printHelp.exit();
process.exit(1);
}
})();
处理命令的大小写
packages/umi/src/service/service.ts
这里开始,只要运行umi xx
都会进入这里
async run2(opts: { name: string; args?: any }) {
let name = opts.name;
if (opts?.args.version || name === 'v') {
name = 'version';
} else if (opts?.args.help || !name || name === 'h') {
name = 'help';
}
// TODO
// initWebpack
return await this.run({ ...opts, name });
}
run方法是插件的生命周期
packages/core/src/service/service.ts
之前也介绍过, umi的插件核心机制,也是umi的核心。详情见umi 生命周期
运行对应的command
packages/preset-umi/src/commands/dev/dev.ts
原文链接:https://juejin.cn/post/7311871555528114230 作者:南蓝