在日常开发中,我们经常会用到脚手架,例如:vue-cli
,react-create-app
,angular-cli
等。使用脚手架可以快速的创建项目,搭建基础开发环境。这篇文章就带大家了解一下,脚手架的工作原理!
本文是在mac环境下开发,文件路径有所不同,复制本文代码,可能无法正常执行,请读者自行判断。主要通过
which [command]
来查看命令的磁盘地址!
Nodejs执行JS文件
新建一个test.js
文件:
console.log('This is a test!');
复制代码
我们可以用node test.js
执行该文件:
我们知道,一个js文件可以通过node
执行,但不能单独执行,这是因为它没有可执行权限。
我们可以通过chmod 777
给js文件赋予可执行权限:
但此时test.js
还是不能直接执行,这是因为js文件需要一个解析器(node)来执行。就像是.py文件需要python解析器一样。
我们可以在test.js文件头部加入#!/usr/bin/env node
这行代码:
#!/usr/bin/env node
console.log('This is a test!');
复制代码
此时我们就可以用./test.js
执行js文件了:
这是为什么呢?
#! Shebang
#!
这个符号在Linux或者Unix中叫做:shebang。
unix类操作系统中一个普通文件带有#!开头的,就会当做一个执行文件来运行,因为#在很多脚本里面是用作注释开头的符号,如果当做执行脚本运行的话,相当于这行就是注释,其实没有什么用,只是标识的作用,说明这个文件可以当做脚本来运行。
#!/usr/bin/env node
表示用node
执行该文件,node
的来源就是/usr/bin/env
用户的环境变量中。
通过命令执行JS文件
我们要怎样才能实现通过一个简单的命令来执行test.js
文件呢?
先来看看vue-cli
是怎么通过vue来执行的吧!
which vue
cd /usr/local/bin
ll
可以看到这个vue命令其实是个软链接(快捷方式),它指向的是../../Users/finget/.config/yarn/global/node_modules/.bin/vue
而这个地址也是软链接,它最终指向@vue/cli/bin/vue.js
最终就是执行的这个vue.js
文件,那同理的我们也可以给test.js
建立一个软链接,通过命令来执行。
cd /Users/finget/.nvm/versions/node/v12.16.1/bin
ln -s /Users/finget/Desktop/test.js finget
我们建立了一个finget
命令来执行test.js
:
finget命令执行全过程:
写我们的第一个命令init
拿到command
我们可以通过node process
拿到命令行的参数列表:
process.argv
属性返回数组,其中包含启动 Node.js 进程时传入的命令行参数。 第一个元素将是 process.execPath
。 第二个元素将是正在执行的 JavaScript 文件的路径。 其余元素将是任何其他命令行参数。
const argv = require('process').argv;
console.log(argv);
const command = argv[2];
console.log(command);
复制代码
拿到options
const options = argv.slice(3)
console.log(options); // [ '--name', 'test' ]
let [option, param] = []
if(options.length) {
[option, param] = options;
option = option.replace('--', '')
console.log(option, param); // name test
}
复制代码
这样我们的第一个命令init
就完成了,能够拿到命令和参数选项,就可以做其他的操作了,这也就是脚手架实现的基本思路。
在实际的脚手架开发中,一般不会采用这么原始的参数读取方式,有yargs
和commander
能够快速搭建起一个基础的脚手架框架。
yargs额外阅读
What’s Yargs?
Yargs helps you build interactive command line tools by parsing arguments and generating an elegant user interface.
初始化test-cli
为了后面的文章做准备,我们新建一个test-cli
项目。
mkdir test-cli
cd test-cli
mkdir bin
npm init -y
cd bin
touch index.js
vim index.js
复制代码
// bin/index.js
#!/usr/bin/env node
console.log('test-cli')
复制代码
上面我们介绍了通过软链接的方式实现通过自定义命令执行js文件,现在我们也可以通过npm link
实现。
// package.json
"bin": {
"test-cli": "bin/index.js"
}
复制代码
cd test-cli
npm link
复制代码
test-cli
就作为命令的名称,bin/index.js
作为该命令执行时的入口文件。
yargs
yarn add yargs
复制代码
// test-cli/bin/index.js
const yargs = require('yargs');
const {hideBin} = require('yargs/helpers');
console.log(hideBin(process.argv));
// 默认情况下 有 --help 和 --version 命令
const arg = hideBin(process.argv);
yargs(arg)
.argv;
复制代码
这样就搭建好了一个最基础的脚手架命令,--help
和--version
是yargs实现的。
usage 用法说明
yargs(arg)
.usage("Usage: $0 <command> [options]")
.strict()
.argv;
复制代码
demandCommand 期望输入的命令个数
yargs(arg)
.usage("Usage: $0 <command> [options]")
.demandCommand(1, "A command is required. Pass --help to see all available commands and options.")
复制代码
alias 别名
yargs(arg)
.usage("Usage: $0 <command> [options]")
.demandCommand(1, "A command is required. Pass --help to see all available commands and options.")
.strict()
.alias("h", "help")
.alias("v", "version")
复制代码
epilog 在页脚注入
yargs(arg)
.usage("Usage: $0 <command> [options]")
.demandCommand(1, "A command is required. Pass --help to see all available commands and options.")
.strict()
.alias("h", "help")
.alias("v", "version")
.epilog('这是我的cli')
复制代码
options
const cli = yargs(argv);
cli
.options({
debug: {
type: 'boolean',
describe: 'Bootstrap debug mode',
alias: 'd'
}
})
复制代码
group 分组
cli
.group(['group1'], 'this is group1')
.group(['group2'], 'this is group2')
复制代码
command
- 参数1: command的格式 command
- 参数2: command的描述 describe
- 参数3: builder 函数,执行command之前执行
- 参数4: handler函数,command实际做的操作
- 参数5: aliases 别名
cli
.command('init [name]', 'Do init a project', (yargs) => {
yargs
.option('name', {
type: 'string',
describe: 'name of a project',
})
}, (argv) => {
console.log(argv)
})
复制代码
recommendCommands
cli
.usage("Usage: $0 <command> [options]")
.demandCommand(1, "A command is required. Pass --help to see all available commands and options.")
.strict()
.recommendCommands()
复制代码
输错命令 会有提示:
parse
const pkg = require("../package.json");
const context = {
myCliVersion: pkg.version,
};
cli
.parse(argv, context);
复制代码
最后
欢迎交流,写的有误的地方,望请纠正!