### 01 前言
>只是你从事前端开发,无论是什么什么级别的程序员都使用过vue或者是react的脚手架。今天这篇文章就让我们一起从零开始搭建一个自己的脚手架。
**脚手架是干什么的?**
拿vue举例,当我们每一次使用脚手架初始化一个项目的时候,里面就已经有了很多的代码。这就是脚手架为我们初始化的标准项目。也就是说,脚手架会为我们做一些重复性,标准化的事情。
**既然已经有了官方的脚手架,我们为什么还要搭建自己的脚手架?**
1、脚手架会为我们定制一个标准化的项目,那么我们在实际的业务线里面也许就会有自己的定制化需求。如果偶尔一个项目定制化没什么,如果公司的一些项目都采用该标准,那么这个定制化的标准项目,在你们内部就可以理解为标准化了。既然是标准化了,那么他就有必要做一个标准(脚手架)。
2、学习。了解敌人的最好方式,就是加入敌人。我们学习使用一门技术的最好方式,就是将这个技术实现一遍。
话不多说,开干!!!
### 02 功能介绍
知己知彼,方能百战不殆。我们实现一个脚手架,先要知道他能有哪些哪些功能,才能去尝试做这些功能:
1、识别控制台的输入
2、控制台交互
3、将我们的模版项目下载下来
识别控制台的输入
类似于我们在控制台输入 --help,控制台要理解我们的输入内容
控制台交互
我们使用 vue create name 创建项目时候,会让我们选择默认创建,还是配置版创建。如果选择配置版创建,又会让我们选择是否配置vuex,vue-router,eslint这些内容。
![在这里插入图片描述](https://img-blog.csdnimg.cn/2021011002262899.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzMjM4NTk5,size_16,color_FFFFFF,t_70#pic_center)
将我们的模版项目下载下来
当我们选择完毕之后,会进行下载,下载失败的判断,断网重连的操作等等一系列。
### 03 项目结构
创建一个目录 cuteXuan-cli
然后 npm init -y
目录下创建 bin, lib 文件夹
在bin 下创建文件 xuan,写如下内容
```
#! /usr/bin/env node
console.log("hello cuteXuan-cli")
```
这个时候可以修改我们的 package.json 文件,我们为它添加了bin属性
```
"name": "cuteXuan-cli",
"version": "1.0.0",
"description": "",
"main": "index.js",
"bin": {
"cuteXuan-cli": "./bin/xuan",
"xuan": "./bin/xuan"
},
```
这个时候我们的最基本结构搭建起来了,是时候执行最重要的一步了:
打开我们的控制台,或者终端。执行下面的命令
```
npm link
// 如果你之前做过类似的操作,也许需要强制这一次的操作
npm link --force
```
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210110022654935.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzMjM4NTk5,size_16,color_FFFFFF,t_70#pic_center)
如果你的控制台,和我的一样(类似),那么恭喜你,你完成了最最重要的一步。
我们已经将我们自己开发的包(脚手架)放到了全局的npm中。也就是说,这个时候,你已经可以像使用vue,react脚手架一样,全局任何位置操作它的命令了。
注:命令就是我们之前配置在package.json 文件中 bin 属性。cuteXuan-cli和xuan 都可以。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210110022715625.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzMjM4NTk5,size_16,color_FFFFFF,t_70#pic_center)
如果你走到这,恭喜你,你的脚手架已经完成了一半了。
### 04 利用Commander解析输入参数
```
npm i commander
```
我们平时使用
vue create xxx
vue --version
vue --help
这一类的命令,如果想要执行后续的操作,就要解析用户的输入参数,当然了,-v ,-h 这一类的简写也要考虑。当然了,这些我们都可以借助Commader 来实现。
```
#! /usr/bin/env node
const program = require('commander')
// 重名,需要强制创建的模式
program
.command('create <app-name')
.description('create a new project')
.option('-f, --force','overwrite target directory if it exists')
.action((name,cmd) => {
console.log(anme,cmd)
})
// xuan -v
program
.version(`cuteXuan-cli @${require('../package.json').version}`)
.usage(`<command> [option]`)
// 解析用户执行命令传入的参数
program.parse(process.argv)
```
简单说明一下,
command 里面输入的是我们控制台执行的命令模版 xuan create xxx
description 一些描述文字
option 一些参数配置及简写形式
action 输入正确命令之后的操作
action 中name 是命令,cmd 是参数,但是它不仅仅🈶️我们的参数,还有各种默认的参数。这就需要进行参数清洗:
```
const clearArgs = (cmd) => {
const args = {}
cmd.options.forEach(element => {
const key = element.long.slice(2)
if (cmd[key]) {
args[key] =cmd[key]
}
});
return args
}
```
这个时候,我们就可以在我们的action 中拿到正确的name和cmd了。
当然了,一个脚手架会有很多这类型的命令,其他的基本上就是苦力活了。而我们这里仅仅写了最重要的 xuan create name 和 xuan --version
### 05 利用Inquirer 与用户做交互
npm i Inquirer
我们在实际的项目中,会遇到下载不同的版本。就比如我们会使用不同的node版本一样。这个时候就需要进行这样的配置。又比如我们需要为我们的新项目提前一些库,比如vuex,vue-router。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210110022818418.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzMjM4NTk5,size_16,color_FFFFFF,t_70#pic_center)
当然了,就像前面说的,我们还需要进行版本的选择,这个时候就需要去我们的代码管理地址,获取我们的版本库列表。
github举例子:
https://api.github.com/users/haimingyue/projectName/tags]
```
async getTags() {
let tags = await fetchTagList()
if (!tags) { return }
tags = tags.map(item => item.name)
let {tag} = await Inquirer.prompt({
name: "tag",
type: 'list',
choices: tags,
message: "please choose a tag to create project"
})
return tag
}
```
上述代码中type: 'list',就会将我们的tags以列表的形式展示。并返回你选择的tags。
当然了,的配置(vuex,vue-router)列表也是同样的意思,只不过它变成了多选,考虑自己试一试吧!!!
06
使用download-git-repo下载配置模版
到了这一步,我们选择好了项目版本,同时也选择好了项目配置。接下来,就可以将选择变现了——下载到本地。
npm install download-git-repo
值得一提的是,download-git-repo 本身是不支持promise。
```
const downloadGitRepo = require('download-git-repo')
const util = require('util')
const newDownloadGitRepo util.promisify(downloadGitRepo)
```
通过上述方法,可以让 download-git-repo 支持promise形式。
```
/**
*
* @param {*} repo 具体模版
* @param {*} tag 模版的版本号
*/
async download(repo,tag) {
let requestUrl = `zhu-cli/${repo}${tag?'#'+tag:''}`
await this.downloadGitRepo(requestUrl,path.resolve(process.cwd),`${repo}@{tag}`)
return this.target
}
```
到了这个时候,简易版的脚手架就搭建完毕了,发布在npm上,就可以供公司内以及公司外的其他人使用了。当然了,真正的脚手架远比这复杂,我们这仅仅是实现了一个最简单不过的脚手架而已。
### 07 总结
开始功能介绍的时候,也许你自己还有些疑惑,读到了这里。让我们一起回顾一下cli。
初始化项目,并利用 npm link 将我们开发的脚手架与全局的npm联系起
使用 Commander 解析用户的输入,这样就可以对用户的输入作出反应
使用 Inquirer 与用户进行交互。如果没有交互,我们也没有做脚手架的必要了,直接给一个链接它不方便吗?
使用 download-git-repo 将用户的选择的内容 download 到本地
![](https://img-blog.csdnimg.cn/img_convert/e1ca573518207f85eccff5a1eed845c1.bmp)
如果感觉这篇文章对你有帮助,点个赞👍,关注一下吧!!!