前言
为了让开发者更加便利,初始化项目的 CLI 工具已经成为各个前端框架、或者解决方案的标配。一行命令就可以初始化一个项目,并且还能选择一些配置项来达到自定制的目的,这样的确提高了开发者的效率,提升了使用体验。但是,随着业务中项目的增多,我们会发现这些并不能足够满足需求。
现状
比如,Vue CLI3 提供了诸多配置选项,还能支持视图,Create-React-App 将配置项隐藏,还能给人快速上手、专注业务的体验。但为什么我们会慢慢地觉得这类脚手架无法满足需求?因为这一类 CLI 的作用就是用来初始化一个全新的项目,但除了初始化全新项目之外,还有很多情况是需要复用以前的项目模版的,这些模板可能安装了一些项目中必须要用到的包、或者包含了一些必要的业务模块,这些是上述 CLI 无法满足的。
我们最常用的复用项目模版的方式就是复制,复制无疑是低效的,那既然如此,我们就可以抽出一套代码专门作为具有针对性的项目模板,抽出来之后,通过一行命令就能直接安装搞定,是不是会更轻松。
还有一点,社区有很多开发者写好的模版就放在 Github 上,我们想用的时候会去 Fork 或者 Clone 到本地来做修改。比如备受好评的 vue-element-admin,模板开发者本身是没有提供类似 CLI 来让使用者更便捷的用上这个模板的。那如果有这样一个工具可以一行命令便能初始化,岂不是更好。
进入正题
说了那么多,无非是为了说明 Mieo 这个 CLI 的出发点以及想要达到的效果。同时更多的是想传达一种思想——在遇到特定场景时,可以想办法去改善现有的状况。我并没有调研过是否有其他类似 CLI 可以做这样一个事情,自己的出发点其实就是为了一个场景,我发现每次自己去初始化一个 React 项目的时候,都需要按照 antd 配置一遍 antd、按需加载、覆盖默认配置、Redux 等等一系列的过程,我很累…很明显其他人也会遇到这种情况,所以我建议有需求的都可以自己造个轮子,实现很简单,但会方便不少,而且想尝试一下用 Node 写命令行程序的朋友也能多一点有意义的实践。
废话不多说,我自己初步实现了这样一个 CLI,目前不完善,但是能用。接下来先简单说一下使用方式,然后讲一讲实现思路、技术细节以及之后能做的事情。Mieo 的项目地址为 https://github.com/seymoe/mieo,有兴趣可以去看看,如果有自己实现的想法可以 Fork 或者直接看,代码量不多,但是我基本做了注释,适合新手向,顺便求 🌟 ~
使用方式
- 安装 Mieo
npm install -g mieo
- 查看帮助与版本
mieo -h
mieo -V
- 选择模版,初始化项目,目前模版还只有两个,mieotpl-egg-mongoo-ts和mieotpl-react-antd-ts
mieo init your_project_name
实现思路
思路很简单,我们要做的就是一个命令行程序,能够通过命令执行一系列操作。命令行执行 init
指令,会去查询现有的模板列表,选择模板开始拉取模版到本地,然后自动进行依赖包的安装,最后提示安装成功,可以去按照模板的相应命令进行开发工作了。
技术细节
可执行文件
为了我们的 mieo
命令能够在命令行执行,那我们需要一个可执行文件,Unix 系统上使用 #!/usr/bin/env node
来进行标示,Windows 可能会有兼容性问题,未测试,有兴趣可以在 Power Shell 上尝试。我们在项目中创建了一个 mieo
文件,然后在 package.json
中指定 bin
选项。
"bin": {
"mieo": "./bin/mieo"
}
这样我们就能本地测试了,在项目中执行 npm link
,将会创建软连接指向我们这个项目,所以我们可以在其他目录下也能执行 mieo
命令了。
接收命令
项目中采用了 commander
这个库,能方便的生成版本和帮助信息,创建 init
命令的方式也很简单。
initProgram() {
this.program = new commander.Command()
// CLI 的版本信息
this.program
.version(pkg.version)
// init 命令
this.program
.command('init <dir>')
.description('Init a project by choosing template.')
.action((dir) => initScript(this, dir))
// 提供系统参数来给 commander 解析
this.program.parse(process.argv)
}
config.json
这个文件记录了所以的模板列表,以供使用者选择。
{
"list": [
{
"name": "mieotpl-egg-mongoo-ts",
"remote": "https://github.com/seymoe/mieotpl-egg-mongoo-ts.git"
}
]
}
init 命令
这个命令接收一个参数,这个参数为文件夹名称。用到了 inquirer
这个库来做用户选择的交互式处理,通过获取 config.json
的数据,供使用者选择,选择之后去看本地的缓存目录是否存在这个模板的源文件,如果有,则比对模板的版本,线上发布的版本比本地的新,则直接进行更新(优化点:给使用者选择的余地),如果版本没有变化则直接拷贝缓存中的文件。然后进入传入的文件夹名称,执行 npm i
的操作(优化点:支持 yarn),安装完成提示。
这里有一点需要注意,因为模版比对方法是 npm view [template name] version --json
,所以为了避免比对有误差,需要保证模版的名字在 npm 库中是唯一的,比如我的就是以 mieotpl-
开头。
具体见 源码
关于模板
目前本人是计划完善两个模版,后面有需求再添加:
- mieotpl-egg-mongoo-ts eggjs、mongoose的ts模板
- mieotpl-react-antd-ts 配好react、antd的ts版本模板(待完成)
总结
在以上的实践里,我们也能发现一些需要优化的点:
- 在模板版本比对出现差异时,应该把选择权交给用户
- 安装依赖时,应该给用户选择用 npm 还是 yarn 的余地
- 执行命令时,没有任何输出,应该优化
- 加载动画
- 流程优化,比如选择模版之后显示更多的模版信息
- 初始化时的文件夹是否已经存在的判断
待优化的点可以解决,但目前对于自己使用已经满足了需求,重点是模版的准备。总体来说,Mieo 只是思路的核心实践,如果真的需要成为一个通用性 CLI,要做的还有很多,相信每个人在实践过程中都会有自己的想法,强烈建议自己尝试一把。
本文首发于 https://www.e7fe.com/post/5d872fc30d44356e1c6c1f70 ,欢迎批评指正。
我也想写个类似的… 思路是: 自由组合包, 通过安装 包 来自动生成webpack配置… 然后我写不下去了XD
@lzszone 呃呃呃,我还是想问通过安装 包 来自动生成webpack配置…有啥思路么😂
@ximolang 我的做法是, 通过包最终能得到一个可以被合并的webpack设置对象, 然后用webpack-merge将若干个包和现有的webpack配置合并, 然后webpack的设置就是一个在内存中的js对象了, 然后问题就是内存 => 文件, 思路是…取到值的类型和值, 生成代码节点, 再插入原来webpack config的语法树里面, 再用代码生成器生成代码, 写入 没解决的问题是 除了基础类型…function和函数调用不是特别好搞, 当时想的是应该也能搞, 但是同种级别的不好弄的点稍微有点多, 感觉做出来也不是特别好用, 就没弄啦, 语法树相关的东西其实我用到的很简单, 用到的库有@babel/generator, @babel/parser, 看看文档就能搞