作者:李旭光 引用请标明出处
前言 这篇文章想写一写前端工程化相关的内容,原因一呢是是结合公司业务给自己定的业绩指标包含这些内容,另外一个原因是因为听了网易前端唐磊说过的一句话,前端leader干什么,很重要的工作就是前端工程化,高级以上不懂前端工程化很难。
今天听了公开课讲到了用node写一个自己的cli,说实话正是工作所需,课程讲的有点快,没有从头跟下来,自己上完了课又上网上找了些资料,终于把步骤弄明白了,下面就把如何自定义一个cli来帮助提升开发效率。同时也完成了一个业务指标,心里美滋滋。
准备 如果你看到这篇文章,也想跟着我的步骤写一下这个自定义cli,那么如下的知识还是有一些为好。
没错就只需要会一些node的基础知识就可以了,接下来正式开始
初始化 首先,我们要给我们的命令行工具起个名字,比如我们熟悉的 vue 命令行就是 vue-cli ,因为我写完了要给公司用,所以我起的名字是 tfd-cli ,你们喜欢叫什么你们随意
首先创建一个名字为 tfd-cli
的文件目录,然后在目录下执行 node
工程的初始化命令
命令执行完成后 tfd-cli
目录下会生成一个我们熟悉的 package.json
文件,我们打开 package.json
文件,增加一段代码,如下
1 2 3 4 "bin": { "tfd": "index.js" }
追加完成后,package.json
文件中的内容是这样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 { "name" : "tfd-cli" , "version" : "1.0.0" , "description" : "" , "main" : "index.js" , "scripts" : { "test" : "echo \"Error: no test specified\" && exit 1" }, "keywords" : [], "author" : "" , "license" : "ISC" , "bin" : { "tfd" : "index.js" } }
也就是说当我们执行 tfd
命令时它就会找同级目录下的 index.js
文件执行其中代码,我们目前还没有 index.js
,文件,那么我们手动创建一个 index.js
的文件,然后在里面写下如下代码
1 2 3 4 #!/usr/bin/env node console .log('hello world' );
执行完这些还不够,因为我们是开发环境所以还有一步操作是要将 tfd
命令告知 npm
,该如何处理,所以我们要执行如下命令
这里如果执行不成功,请用管理员权限执行,执行完成后我们会得到一个 package-lock.json
的文件,内容如下
1 2 3 4 5 { "name" : "tfd-cli" , "version" : "1.0.0" , "lockfileVersion" : 1 }
如此一来,我们就可以在任何目录下执行命令行 tfd
就会执行 index.js
文件了,这里我们会在控制面板中输出 hello world
,怎么样是不是小有成就感,我们接着往下来。
创建指令 我们写个命令行工具肯定不是为了输出个 hello world
这么简单,而是希望通过用户输入内容后根据条件输出一些东西,那么让我们想想一个命令行工具应该具备哪些指令呢?
查看命令行工具版本
查看帮助文档
初始化模板
列出模板类型
等等
那么用指令该如何描述呢
1 2 3 4 tfd -V|--version //查看工具版本号 tfd -h|--help //查看使用帮助 tfd init <template-name> <project-name> //基于指定模板进行项目初始化 tfd list //列出所有可用模板
为了执行命令,这里我们要引入一个 node
包叫做 commander
,因此我们要先执行一下 install
命令
接着我们就可以在 index.js
里面写指令了。
1 2 3 4 5 6 7 8 #!/usr/bin/env node const cmd = require ('commander' );cmd.version('0.1.0' );
到这一步我们在控制台敲一下 tfd -V
你会发现什么也没输出,这是因为到这一步我们还无法解析 tfd -V
操作,在这之前我们要知道一个命令
当我们把这句话加到 console.log
中在 index.js
中输出时你会看到控制台打印出
1 2 3 4 5 6 7 8 9 10 11 12 #!/usr/bin/env node const cmd = require ('commander' );cmd.version('0.1.0' ); console .log(process.argv)[ '/usr/local/bin/node' , '/usr/local/bin/tfd' , '-V' ]
接下来我们要让commander
获取参数执行命令
1 2 3 4 5 6 7 8 9 10 #!/usr/bin/env node const cmd = require ('commander' );cmd.version('0.1.0' ); cmd.parse(process.argv);
这个时候我们再在控制台输入 tfd -V
时,我们就会发现,控制台输出了 0.1.0
,这样我们就完成了查版本的指令,接下来我们完成其他的指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #!/usr/bin/env node const cmd = require ('commander' );cmd.version('0.1.0' ); cmd .command('init <template> <project>' ) .description('初始化项目模板' ) .action((templateName, projectName ) => { console .log(templateName, projectName); }) cmd .command('list' ) .description('查看所有可用模板' ) .action(() => { console .log(` a a模板 b b模板 c c模板 ` ) }) cmd.parse(process.argv);
这个时候我们在控制台上输入 tfd -h
的时候,控制台会输出如下代码
1 2 3 4 5 6 7 8 9 Usage: tfd [options] [command] Options: -V, --version output the version number -h, --help output usage information Commands: init <template> <project > 初始化项目模板 list 查看所有可用模板
这样我们就实现了自定义命令,我们执行一下 tfd init template1 project1
,我们可以看到,控制台中输出了 template1 project1
,也就是说command
命令后尖括号中指向了action
中的参数,我们就可以通过判断action
中的参数做具体的操作了。
通常模板可以选择从本地拷贝一份,但更常用的是从线上拷贝一份,比如从github
中,接下来我们就看看如何从github
中拷贝一个模板作为项目的初始化工程
github上创建模板仓库 首先我们要在github上创建两个仓库 tpl-1
tpl-2
,这里为了从github
中下载仓库我们需要一个node
包支持,让我们请出download-git-repo
,别忘了执行安装命令
1 npm install download-git-repo
安装完依赖之后让我们再去index.js
填点东西,首先引入下载依赖,然后是创建下载的 template
抽象对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #!/usr/bin/env node const cmd = require ('commander' );const download = require ('download-git-repo' );const templates = { 'tpl-1' : { url: 'https://github.com/lixuguang/tpl-1' , downloadUrl: 'https://github.com:lixuguang/tpl-1#master' , description: 'tfd-cli脚手架测试模板1' }, 'tpl-2' : { url: 'https://github.com/lixuguang/tpl-2' , downloadUrl: 'https://github.com:lixuguang/tpl-2#master' , description: 'tfd-cli脚手架测试模板2' } } cmd.version('0.1.0' ); cmd .command('init <template> <project>' ) .description('初始化项目模板' ) .action((templateName, projectName ) => { let {downloadUrl} = templates[templateName]; download(downloadUrl, projectName, {clone : true }, err => { if (err){ console .log('模板下载失败' ); }else { console .log('模板下载成功' ); } }) }) cmd .command('list' ) .description('查看所有可用模板' ) .action(() => { const templateName = Object .keys(templates) console .log(templateName) }) cmd.parse(process.argv);
这样当我们执行 tfd list
就可以看到有哪些模板了,然后执行tfd init tpl-1 newproject
就可以依据 tpl-1
模板创建出 newproject
工程,这个过程实际上就是从github
仓库克隆一份tpl-1
作为模板创建工程newproject
这里需要注意的是download地址跟github仓库地址有点出入,比如github仓库地址是https://github.com/xxx/xxx而下载地址是https://github.com:xxx/xxx
虽然这样执行完成后就完成了基本的cli的雏形,但是毕竟不灵活,我们在使用vue-cli
时,它的创建过程是问答式和选择式的,另外每个过程都会有进度显示什么的,那么要怎么添加这些功能呢,我们接着往下做。
进阶增加功能 使用inquirer
进行命令行答询 inquirer
是一个进行命令行答询的库,通过它我们就可以创建问答式的内容,首先还是安装依赖
使用handlebars修改package.json 我们都知道在使用vue-cli
的初始化命令后,会在项目目录下生成一个package.json
文件,它就像是这个项目的基因序列一样,影响着项目的整个结构。模板是固定的,那要修改其中的package.json
符合自己项目的需要,就要用到handlebars
这个库来改写package.json
文件,老规矩先安装它
使用ora在命令行中显示加载状态 我们在装任何依赖时都会有进度条显示进度,如果没有进度条又没有任何响应,会让用户迷茫,为了友好,我们就要加进度条,这里我们需要引入ora
这个库来完成进度显示,我们接着安装。
使用chalk和log-symbols增加命令行输出样式 为了让命令行有红红绿绿的效果以及符号效果,我们需要使用chalk
和log-symbols
来丰富样式,少废话,接着装
1 npm install chalk log-symbols
集大成 终于安装完一堆的依赖,别忘了在index.js
中引入,让我们看看具体如何使用这些库吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 #!/usr/bin/env node const cmd = require ('commander' );const download = require ('download-git-repo' );const iq = require ('inquirer' ); const hb = require ('handlebars' ); const ora = require ('ora' ); const chalk = require ('chalk' ); const ls = require ('log-symbols' ); const fs = require ('fs' ); const templates = { 'tpl-1' : { url: 'https://github.com/lixuguang/tpl-1' , downloadUrl: 'https://github.com:lixuguang/tpl-1#master' , description: 'tfd-cli脚手架测试模板1' }, 'tpl-2' : { url: 'https://github.com/lixuguang/tpl-2' , downloadUrl: 'https://github.com:lixuguang/tpl-2#master' , description: 'tfd-cli脚手架测试模板2' } } cmd.version('0.1.0' ); cmd .command('init <template> <project>' ) .description('初始化项目模板' ) .action((templateName, projectName ) => { let {downloadUrl} = templates[templateName]; const loading = ora('模板下载中...' ).start(); download(downloadUrl, projectName, {clone : true }, err => { if (err){ loading.fail('模板下载失败' ); }else { spinner.succeed('模板下载成功' ); iq.prompt([ { type: 'input' , name: 'name' , message: '请输入项目名称' , default : projectName }, { type: 'input' , name: 'description' , message: '请输入项目简介' , default : '' }, { type: 'input' , name: 'author' , message: '请输入作者名称' , default : '' } ]).then(answers => { let packageContent = fs.readFileSync(`${projectName} /package.json` , 'utf8' ); let packageResult = hb.compile(packageContent)(answers); fs.writeFileSync(`${projectName} /package.json` , packageResult); console .log(ls.success, chalk.green('模板项目文件准备成功!' )); }) } }) }) cmd .command('list' ) .description('查看所有可用模板' ) .action(() => { const templateName = Object .keys(templates) console .log(templateName) }) cmd.parse(process.argv);
到这里你自己的命令行工具就创建完成了,当然还可以继续丰富,比如加上选择条件等,这个就更复杂了,不在本文中展开,后续会继续改进这个命令行工具,当然如果要将自己的命令行工具给其他人用当然要先发布出去,就像我们要用vue-cli
,首先要在npm
上下载下来,同样的我们要想让别人用,就得先上传到npm
上。
发布到npm上 首先你需要有一个npm的账号,没有赶快去注册一个,在控制台中输入npm login
,它会让你依次输入 Username
Password
Email
,当你都按照要求输入完之后,成功的话你会获得如下信息Logged in as XXX on https://registry.npm.org/.
,再接下来执行 npm publish
命令,你的自定义脚手架就会发布到npm
上,供他人下载使用,怎么样,学会了么?
后记 这篇文章会有后续持续进化跟进,多篇文章连续,只要我的cli工具还在进化,文章就会继续,欢迎跟进。