给我们小团队撸个脚手架 —— 初识脚手架

1,827 阅读4分钟

在日常开发中,我们经常会用到脚手架,例如:vue-clireact-create-appangular-cli等。使用脚手架可以快速的创建项目,搭建基础开发环境。这篇文章就带大家了解一下,脚手架的工作原理!

本文是在mac环境下开发,文件路径有所不同,复制本文代码,可能无法正常执行,请读者自行判断。主要通过which [command]来查看命令的磁盘地址!

Nodejs执行JS文件

新建一个test.js文件:

console.log('This is a test!');

我们可以用node test.js执行该文件:

image.png

我们知道,一个js文件可以通过node执行,但不能单独执行,这是因为它没有可执行权限。 我们可以通过chmod 777给js文件赋予可执行权限:

image.png

但此时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文件了:

image.png

这是为什么呢?

#! 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

image.png

可以看到这个vue命令其实是个软链接(快捷方式),它指向的是../../Users/finget/.config/yarn/global/node_modules/.bin/vue 而这个地址也是软链接,它最终指向@vue/cli/bin/vue.js

image.png

image.png

最终就是执行的这个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

image.png

finget命令执行全过程:

image.png

写我们的第一个命令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);

image.png

拿到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
}

image.png

这样我们的第一个命令init就完成了,能够拿到命令和参数选项,就可以做其他的操作了,这也就是脚手架实现的基本思路。

在实际的脚手架开发中,一般不会采用这么原始的参数读取方式,有yargscommander能够快速搭建起一个基础的脚手架框架。

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 作为该命令执行时的入口文件。

image.png

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实现的。

image.png

image.png

usage 用法说明

yargs(arg)
  .usage("Usage: $0 <command> [options]")
  .strict()
  .argv;

image.png

demandCommand 期望输入的命令个数

yargs(arg)
  .usage("Usage: $0 <command> [options]")
  .demandCommand(1, "A command is required. Pass --help to see all available commands and options.")

image.png

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")

image.png

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')

image.png

options

const cli = yargs(argv);
cli
  .options({
    debug: {
      type: 'boolean',
      describe: 'Bootstrap debug mode',
      alias: 'd'
    }
  }) 

image.png

group 分组

cli
  .group(['group1'], 'this is group1')
  .group(['group2'], 'this is group2')

image.png

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)
  })

image.png

recommendCommands

cli
  .usage("Usage: $0 <command> [options]")
  .demandCommand(1, "A command is required. Pass --help to see all available commands and options.")
  .strict()
  .recommendCommands()

输错命令 会有提示:

image.png

parse

const pkg = require("../package.json");
const context = {
  myCliVersion: pkg.version,
};

cli
 .parse(argv, context);

image.png

最后

欢迎交流,写的有误的地方,望请纠正!

仓库地址:gitee.com/finget/test…

【给我们小团队撸个脚手架 —— 通过脚手架下载模板】