一、CLI有啥用,认识CLI
前端开发过程中常见的CLI有:
- create-react-app
- vue-cli
- webpack-cli
- prettier-cli
基本复杂一点的工具都在集成CLI,为啥都要搞成CLI呢?
因为CLI可以提供更强大的功能:
- 通过命令搭配实现不同的功能
- 管理项目模版
- 启动本地服务
- 生成模版文件
- 对代码进行格式化
二、搭建一个最简单的CLI
我们先搭建一个最简单的CLI来体验下,然后逐步实现复杂点的功能。
1、新建项目
首先新建项目作为CLI源代码地址
mkdir cli-demo
cd cli-demo
npm init
2、安装依赖
yargs文档配置:该插件将用户通过终端输入的参数解析成对象,可以配置各种参数。
yarn add yargs
命令使用语法:
.command(cmd, desc, [builder], [handler])
3、CLI命令配置
下面的配置说明:
- 配置命令
get - 声明信息
make a get HTTP request - 并配置了参数
url信息 - 最后callback函数获取用户执行的参数
具体使用:编辑index.js,第一行一定要加注释,表明运行在node环境下
#!/usr/bin/env node
const yargs = require('yargs/yargs')
const { hideBin } = require('yargs/helpers')
yargs(hideBin(process.argv))
.command(
'get',
'make a get HTTP request',
function (yargs) {
return yargs.options({
url: {
alias: 'u',
describe: 'the URL to make an HTTP request to'
}
})
},
function (argv) {
console.log("callback", argv.url)
}
)
.help()
.argv
执行命令,可以获取输入的参数
这里get就是定义的指令,url是指令下的key值,用--url输入,alias就是别名,用-u表示,后面跟要输入的参数。
3、npm发布
接着我们发布下npm,然后一个CLI就完成了。
登陆npm仓库,没有的话去注册一个,地址
npm login
选择一个中意的cli名字,查一下是否存在,这里我们起个名字cli-demo3
执行npm version patch && npm publish --registry=https://registry.npmjs.org,不出意外的话,就发布成功了。
4、下载使用
然后我们本地使用下,首先下载到本地,全局下载npm install -g cli-demo3
cli-demo3是我们前期测试使用写的CLI,后面继续使用我正常部署的CLI:u-amin-cli,原理是一样的
三、终端交互优化
1、log-symbols:输出结果用图标标识
# 这里使用4版本,升级到5版本不再支持require方式导入
yarn add log-symbols@4.1.0
使用方法
使用效果
2、chalk:打印出各种颜色的文案提示,功能强大,几乎支持所有的颜色
# 这里使用4版本,升级到5版本不再支持require方式导入
yarn add chalk@4.1.2
1、支持传入string,array和模版字符串
使用方法
console.log(chalk.blue("this is blue"))
console.log(chalk.blue('Hello', 'World!', 'Foo', 'bar', 'biz', 'baz'));
console.log(`
CPU: ${chalk.red('90%')}
RAM: ${chalk.green('40%')}
DISK: ${chalk.yellow('70%')}
`);
使用效果
2、支持修改背景色、各种颜色格式、指定不同文本的颜色
使用方法
console.log(chalk.blue.bgRed.bold('Hello world!'));
console.log(chalk.red('Hello',
chalk.underline.yellowBright('world') + '!'));
console.log(chalk.hex('#DEADED').underline('Hello, world!'))
console.log(chalk.rgb(15, 100, 204).inverse('Hello!'))
使用效果
3、支持自行封装一些颜色
使用方法
const error = chalk.bold.red;
const warning = chalk.hex('#FFA500'); // Orange color
console.log(error('Error!'));
console.log(warning('Warning!'));
使用效果
3、ora:增加loading效果
# 这里使用5版本,升级到6版本不再支持require方式导入
yarn add ora@5.4.1
使用方法
const ora = require('ora');
const spinner = ora('Loading unicorns').start();
setTimeout(() => {
spinner.color = 'yellow';
spinner.text = 'Loading rainbows';
}, 1000);
使用效果
4、inquirer:在终端选择命令
使用方法
var inquirer = require('inquirer');
inquirer
.prompt([
{
name: "templateType",
type: "list",
default: "vue",
choices: [
{
name: "React",
value: "react",
},
],
message: "Select the template type.",
}
])
.then((answers) => {
// Use user feedback for... whatever!!
})
.catch((error) => {
if (error.isTtyError) {
// Prompt couldn't be rendered in the current environment
} else {
// Something else went wrong
}
});
使用效果
这样就可以给客户提示选择来处理逻辑。
5、download-git-repo:download Git仓库
使用方法
const download = require("download-git-repo");
download('direct:https://gitlab.com/flippidippi/download-git-repo-fixture.git#my-branch', 'test/tmp', { clone: true }, function (err) {
console.log(err ? 'Error' : 'Success')
})
还有很多类似的插件,来丰富cli的功能,根据需要添加即可
- open 打开浏览器
- yargs/commander 执行cli命令
- get-port 获取当前端口号
四、CLI优化
1、提取命令行配置到一个文件中
启动文件index.js
const yargs = require('yargs/yargs')
const { hideBin } = require('yargs/helpers')
const config = require('./config')
const yargsCommand = yargs(hideBin(process.argv))
config.forEach(commandConfig => {
const { command, descriptions, options, callback } = commandConfig
yargsCommand.command(
command,
descriptions,
yargs => yargs.options(options),
(argv, ...rest) => {
callback(argv, ...rest);
}
)
})
yargsCommand.help().argv
指令文件config.js
const commandOptions = [
{
command: "create",
descriptions: "拉取一个项目模版",
options: {
name: {
alias: "n",
type: "string",
require: true,
describe: "项目名称",
},
},
callback: async (argv) => {
create({name: argv.name})
}
}]
提取配置,就很清晰了
2、配置ESLint和Prettier,之前专门写了自动化检查代码的文章。
前端工程化:Prettier+ESLint+lint-staged+commitlint+Hooks+CI 自动化配置处理
3、提取脚本生成使用文档
上一步我们对配置进行了提取,接着根据配置生成使用文档,如下
使用方法
yarn add json2md@1.12.0 -D
const json2md = require("json2md")
// 按json2md需要的数据格式组合
const data = json2md([
{ h1: "JSON To Markdown" }
, { blockquote: "A JSON to Markdown converter." }
]))
// 写入Readme.md文档
fs.writeFile(path.join(__dirname, "../Readme.md"), json2md(data), (err) => {
if (err) throw err;
console.log("update docs success");
});
五、CLI管理模版项目
1、配置下载命令
{
command: "create",
descriptions: "拉取一个项目模版",
options: {
name: {
alias: "n",
type: "string",
require: true,
describe: "项目名称",
},
},
callback: async (argv) => {
create({name: argv.name})
}
}
2、准备模版文件列表
const map = {
vue: "https://github.com/luchx/ECHI_VUE_CLI3.0.git",
react: "git@github.com:richLpf/antd-template-demo.git#main"
}
3、下载逻辑
1、inquirer提示用户选择对应的模版,具体可以看前面用法
2、通过download下载模版文件
module.exports = function downloadFromRemote(url, name) {
return new Promise((resolve, reject) => {
download(`direct:${url}`, name, { clone: true }, function (err) {
if (err) {
reject(err);
return;
}
resolve();
});
});
};
3、通过ora在下载过程中展示loading
这是主要的下载逻辑,关于日志,我们可以更好的展示,也可以配置更复杂的命令:替换下载的项目名称,删除.git目录,修改package.json配置
六、通过CLI启动API模拟接口服务
前端在开发的过程中,总会遇到后端接口无法按时提供的情况,这种情况下有很多解决方案,常见的通过客户端生成模拟api,在项目中引入Mock。
这里我们通过CLI来实现一个更加简单好用的模拟方案
1、方案原理
通过CLI启动一个或多个服务器,通过Express做路由转发本地的JSON文件
2、命令配置
首先我们配置mock命令,设定参数type、port、create分别代表API类型、启动Http服务器端口号,默认9000,create是否自动生成接口数据JSON文件。
{
command: "mock",
descriptions: "启动一个本地服务,模拟返回接口数据",
options: {
type: {
alias: "t",
type: "string",
default: "action",
describe: "选择API类型",
choices: ['action', 'restful']
},
port: {
alias: "P",
type: "number",
default: 9000,
describe: "选择启动的端口号",
},
create: {
alias: "c",
type: "boolean",
default: false,
describe: "如果mock目录不存在是否自动创建,默认不自动创建"
},
},
callback: async (argv) => {
mock({
...argv
})
}
3、Http服务处理
1、启动express服务器,配置参数解析和跨域设置,并获取mock数据的目录,启动Http服务器
const app = express()
app.use(bodyParser.json({limit: '50mb'}))
app.use(express.urlencoded({ extended: true }));
app.all('*', (req, res, next) => {
res.header('Access-Control-Allow-Origin', '*'); //访问控制允许来源:所有
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); //访问控制允许报头 X-Requested-With: xhr请求
res.header('Access-Control-Allow-Metheds', 'PUT, POST, GET, DELETE, OPTIONS'); //访问控制允许方法
res.header('X-Powered-By', 'nodejs'); //自定义头信息,表示服务端用nodejs
res.header('Content-Type', 'application/json;charset=utf-8');
next();
});
const filePath = path.join(process.cwd(), `./mock/`)
app.listen(port, () => console.log(`Mock api listening on port ${port}!`));
2、如果是Action类型的API
比如请求url为:
http://localhost:9000/acl
请求体为:
{
Action: "GetUsers"
}
此时在CLI执行的目录,新建mock/acl/GetUsers.json
GetUsers.json内容为返回的json数据
{
"RetCode": 0,
"Message": "this is error",
"Data": []
}
然后通过fs模块读取json文件的数据,直接返回模拟的数据,因为是实时读取的,所以更新json数据不需要重启服务。主要逻辑如下
app.post('*', async(req, res) => {
const key = req.params[0].substring(1)
const { Action } = req.body
const file = `${filePath}${key}/${Action}.json`;
fs.readFile(file, 'utf-8', function(err, data) {
if (err) {
res.send(NotFoundResponse);
} else {
res.send(data);
}
})
})
3、如果是Restful类型的API
比如请求url为:
http://localhost:9000/acl/users
post请求体为:
{
limit: 10
}
此时在CLI执行的目录,新建mock/acl/users/post.json
post.json内容为返回的json数据
{
"RetCode": 0,
"Message": "get users",
"Data": []
}
如果是get请求,只需要添加get.json就可以了。
然后同样通过fs读取对应的json数据
app.all("*", async(req, res) => {
const key = req.params[0]
const method = req.method
const file =`${filePath}${key}/${method}.json`
console.log("file", file)
fs.readFile(file, 'utf-8', function(err, data) {
if (err) {
res.send(NotFoundResponse);
} else {
res.send(data);
}
})
})
这样就完成了CLI启动一个模拟的http服务,用起来很方便,并且可以支持直接在项目中使用。
具体可以试试看u-admin-cli
对于大部分mock来说,复杂的配置是不需要的,这里仅仅是用了固定的json数据,我觉得就够用了,当然还可以自行处理数据,引入Mock规则,自行定义生成数据类型等
以上就是搭建一个CLI的过程,我们从最简单的开始构建了一个基础CLI,在这个基础上进行了交互优化,然后实际开发了CLI的两个功能:
- 1、通过CLI管理部门内的模版项目
- 2、通过CLI启动Http服务器,模拟接口生成
CLI地址:www.npmjs.com/package/u-a…
码字不易,欢迎点赞👍,有问题请留言。