4-1 脚手架命令注册 & 高性能脚手架架构设计
1 一周导读
1.1 标题
- 基于
Commander
完成脚手架命令注册和命令执行过程开发
1.2 将收获什么
- 如何设计高性能脚手架
Node
多进程开发javascript
面向对象的实战技巧
1.3 主要内容
- 图解高性能脚手架架构设计方法
- 封装通用的
Package
和Command
类 - 基于缓存 +
Node
多进程实现动态命令加载和执行 - 将业务逻辑和脚手架框架彻底解耦
1.4 附赠内容
Node
多进程开发进阶 --child_process
源码分析- 深入
Node
源码看清spawn/exec/execFile/fork
的本质区别,彻底搞懂Node
多进程原理
- 深入
1.5 关键词
- 高性能/可扩展的脚手架 - 利用缓存提升脚手架性能并解耦业务逻辑
- 面向对象 - 利用
Class
完成javascript
面向对象编程 Node
多进程 - 深入Node
多进程原理
1.6 学习方法
- 学以致用: 将前两周中学到的知识进行实际应用(
commander/Lerna
命令执行原理) - 知识储备: 面向对象、
ES6
新特性Class
、shell
脚本(macOS
)bat
批处理文件等概念需要预先储备 - 充分实践:
Node
多进程是一门较为复杂的技术,需要同学们充分实践和思考
1.7 注意事项
Node
多进程高能预警
2 imooc-cli 脚手架命令注册
2.1 imooc-cli脚手架初始化
const commander = require('commander')
const program = new commander.Command()
function registerCommand() {
program
.name(Object.keys(pkg.bin)[0])
.usage('<command> [options]')
.version(pkg.version)
program.parse(process.argv)
}
2.2 全局参数注册
1. 实现debug模式
function registerCommand() {
program
.name(Object.keys(pkg.bin)[0])
.usage('<command> [options]')
.version(pkg.version)
.option('-d, --debug', '是否开启调试模式', false)
program.on('option:debug', function() {
process.env.LOG_LEVEL = 'verbose'
log.level = process.env.LOG_LEVEL
log.verbose('test')
})
program.parse(process.argv)
}
2. 对未知命令进行监听
function registerCommand() {
program
.name(Object.keys(pkg.bin)[0])
.usage('<command> [options]')
.version(pkg.version)
// 对未知命令的监听
program.on('command:*', function(obj) {
const availableCommands = program.commands.map(cmd => cmd.name())
console.log(colors.red('未知的命令:' + obj[0]));
availableCommands.length
&& console.log(colors.red('可用命令:' + availableCommands.join(', ')));
})
program.parse(process.argv)
}
3. 自定义帮助文档
function registerCommand() {
program
.name(Object.keys(pkg.bin)[0])
.usage('<command> [options]')
.version(pkg.version)
if(process.argv.length < 3) {
program.outputHelp()
console.log();
} else {
program.parse(process.argv)
}
}
未输入命令时也要打印帮助文档
function registerCommand() {
program
.name(Object.keys(pkg.bin)[0])
.usage('<command> [options]')
.version(pkg.version)
program.parse(process.argv)
if(program.args && program.args.length < 1) {
program.outputHelp()
}
}
2.3 imooc-cli脚手架命令注册
1. 简易版
function registerCommand() {
program
.command('init [projectName]')
.option('-f, --force', '是否强制初始化项目')
.action((projectName, cmdObj) => {
console.log('init', projectName, cmdObj.force)
})
program.parse(process.argv)
if(program.args && program.args.length < 1) {
program.outputHelp()
}
}
2. 分包版
- commands>init>lib>index.js
lerna create init
'use strict';
function init(projectName, cmdObj) {
console.log('init', projectName, cmdObj.force);
}
module.exports = init
commands>init: npm link
- core>cli>package.json
"dependencies": {
"@zmoon-cli-dev/init": "file:../../commands/init",
}
core: npm link
- core>cli>lib>index.js
const init = require('@zmoon-cli-dev/init')
function registerCommand() {
program
.command('init [projectName]')
.option('-f, --force', '是否强制初始化项目')
.action(init)
program.parse(process.argv)
if(program.args && program.args.length < 1) {
program.outputHelp()
}
}
zmoon-cli-dev init test-project --force
3 高性能脚手架架构设计
3.1 痛点分析

-
存在的问题:
- cli安装速度慢:所有
package
都集成在cli
里,因此当命令较多时,会减慢cli
的安装速度 - 灵活性差:
init
命令只能使用@zmoon-cli-dev/init
包,对于集团公司而言,每个bu
的init
命令可能都各不相同,可能需要实现init
命令动态化,如:- 团队A使用
@zmoon-cli-dev/init
作为初始化模块 - 团队B使用自己开发的
@zmoon-cli-dev/my-init
作为初始化模块 - 团队c使用自己开发的
@zmoon-cli-dev/your-init
作为初始化模块
- 团队A使用
- cli安装速度慢:所有
-
要求我们能够动态加载
init
模块,这将增加架构的复杂度,但大大提升脚手架的可扩展性,将脚手架框架和业务逻辑解耦
3.2 脚手架架构优化
-
优化的点:
-
脚手架命令动态加载
-
脚手架命令使用缓存
-
脚手架命令动态加载完使用
node
多进程执行
-
分类:
前端路线 / ⑥ 前端架构师
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)