最近有在折腾本身的脚手架,没搞过这玩意,查阅了大量文章,有所收获,因而有了这篇记录。但愿对一些童鞋有所帮助。前端
要了解前端脚手架的原理,能够拿咱们最熟悉的 vue-cli
这个脚手架来讲说,使用过的童鞋都知道使用脚手架建立 vue 项目时,须要使用命令:vue
vue create 项目名
复制代码
你是否想过这句命令的原理呢,当这句命令执行时,它在背后到底进行了怎样的操做呢?node
当咱们在电脑上安装完 vue-cli
后,就可使用一个 vue
的命令。在使用命令前,咱们能够先查看一下帮助信息,在终端中输入 vue --help
命令就能够看到一些帮助信息,以下:linux
$ vue --help
Usage: vue <command> [options]
Options:
-V, --version output the version number
-h, --help output usage information
Commands:
create [options] <app-name> create a new project powered by vue-cli-service
add [options] <plugin> [pluginOptions] install a plugin and invoke its generator in an already created project
invoke [options] <plugin> [pluginOptions] invoke the generator of a plugin in an already created project
inspect [options] [paths...] inspect the webpack config in a project with vue-cli-service
serve [options] [entry] serve a .js or .vue file in development mode with zero config
build [options] [entry] build a .js or .vue file in production mode with zero config
ui [options] start and open the vue-cli ui
init [options] <template> <app-name> generate a project from a remote template (legacy API, requires @vue/cli-init)
config [options] [value] inspect and modify the config
outdated [options] (experimental) check for outdated vue cli service / plugins
upgrade [options] [plugin-name] (experimental) upgrade vue cli service / plugins
migrate [options] [plugin-name] (experimental) run migrator for an already-installed cli plugin
info print debugging information about your environment
Run vue <command> --help for detailed usage of given command.
复制代码
从输出信息上能够看到 vue
是主命令,当咱们须要使用脚手架的一些操做命令时,须要以vue <command> [options]
这样的方式去调用, 全部的操做都是基于这个主命令拓展的,好比 --help
,它属于 Options
,能够输出帮助文档, 又好比 create
命令,他属于 Commands
,它也有本身的使用格式,可使用命令 vue create --help
查看,以下:webpack
$ vue create --help
Usage: create [options] <app-name>
create a new project powered by vue-cli-service
Options:
-p, --preset <presetName> Skip prompts and use saved or remote preset
-d, --default Skip prompts and use default preset
-i, --inlinePreset <json> Skip prompts and use inline JSON string as preset
-m, --packageManager <command> Use specified npm client when installing dependencies
-r, --registry <url> Use specified npm registry when installing dependencies (only for npm)
-g, --git [message] Force git initialization with initial commit message
-n, --no-git Skip git initialization
-f, --force Overwrite target directory if it exists
--merge Merge target directory if it exists
-c, --clone Use git clone when fetching remote preset
-x, --proxy <proxyUrl> Use specified proxy when creating project
-b, --bare Scaffold project without beginner instructions
--skipGetStarted Skip displaying "Get started" instructions
-h, --help output usage information
复制代码
能够看到,即便是一个子命令,它的使用参数也是不少的,由此也说明了 vue-cli
脚手架在背后作了不少工做,可是咱们也没必要惊慌于脚手架的复杂,任何复杂的东西都不是一步到位的,咱们能够先看看最基础的 vue
命令是怎么实现的。git
在终端能够直接执行 vue
命令,说明它是一个全局命令,在个人 mac 电脑上,可使用 which vue
命令来查找它的位置:github
$ which vue
/usr/local/bin/vue
复制代码
/usr/local/bin/
目录存放了不少全局命令变量,包括 vue
,咱们能够在这个目录下使用 ll vue
来看下 vue
命令的详情:web
$ ll vue
lrwxr-xr-x 1 wangjian admin 39B 6 2 14:17 vue -> ../lib/node_modules/@vue/cli/bin/vue.js
复制代码
能够看到,vue
实际上是一个软连接,它指向的地址是../lib/node_modules/@vue/cli/bin/vue.js
,这个地址实际上是 npm
全局安装的包的存放地址。还记得咱们安装脚手架时的包名就是 @vue/cli
吗,它在全局注册的 vue
命令其实就是指向这个包文件下的 bin/vue.js
文件。如今咱们明白了使用命令 vue
时,执行的程序是 vue.js
。其实就是:vue-cli
vue -V
// 等同于下面
/usr/local/lib/node_modules/@vue/cli/bin/vue.js -V
复制代码
/usr/local/lib/node_modules/ 是我电脑上 npm 全局安装包的存放位置shell
那么问题来了,vue.js
是怎么运行起来的?js 文件是不能直接在终端中执行的。
js 文件主要有两种执行方式,一个是在 web 页面中执行,另外一个就是使用 node 执行。很显然上述 vue.js
文件的执行应该是属于第二种,即运行在 node 环境中。那么是在哪里执行的呢,咱们并无找到相似于 node. vue.js
这样的执行入口。
其实答案就在 vue.js
文件自己。当咱们打开这个文件就能够看到文件开头第一行写着:
#!/usr/bin/env node
复制代码
这句代码的做用就是会在所处电脑系统上自动查找 node
变量,而后使用 node
来执行当前脚本文件。意思就是说当 js 文件的头部加上这一句后,就可使用 ./
的形式来执行这个文件。能够测试一下,写一个简单的 js 脚本,就叫 test.js
吧:
#!/usr/bin/env node
console.log("hello world")
复制代码
注意,若是是 mac 系统或者 linux 系统,须要给这个文件赋权 chmod 755 test.js
,而后就能够执行了,以下:
可以在终端直接调用 js 文件是开发脚手架的一个关键知识点,下面咱们就尝试作一个简单的脚手架。
咱们能够尝试搭建一个简单的脚手架雏形,它的功能没必要完备,只要咱们理解了开发流程就能够本身拓展。
随便找一个目录建立项目,而后 npm 初始化:
mkdir my-cli
cd my-cli
npm init -y
复制代码
而后在项目目录中新建一个入口文件 index.js
(名字随意),在文件中随意写上一些 js 语句,记得首行要加上 #!/usr/bin/env node
,像这样:
#! /usr/bin/env node
console.log("This is my cli tool")
复制代码
此时项目结构很简单:
my-cli
├── index.js
└── package.json
复制代码
咱们还须要在 package.json
文件中配置 bin
选项,它是配置各个内部命令对应的可执行文件的位置,修改以下:
{
"name": "my-cli",
"version": "1.0.0",
"description": "",
"main": "index.js",
"bin": {
"hi": "index.js" // 此处添加 bin 命令
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
复制代码
bin
能够配置成一个对象,每个 key 名都会被注册成一个命令名,这里我配置成 hi
命令。说到这里引出一个问题,你知道为何咱们安装脚手架时安装的包是 @vue/cli
,但最后执行的倒是 vue
命令吗?就是由于 vue-cli 脚手架在注册 bin 命令时,key 名为 vue
。
此时我但愿在终端输入 hi
时,就能够执行 index.js
文件,当咱们把这个项目发布到 npm 上,而后本地全局安装后能够实现这样的功能,但咱们本地调试有一个更简单的方法,就是使用 npm link
,它能够将项目连接到全局,达到等同于全局安装的效果。在项目中执行 npm link
,以下:
mac 电脑须要加上 sudo 执行。此时咱们查看下全局环境下是否有 hi
命令:
有了,再使用 ll /usr/local/bin/hi
查看下详细信息:
图中能够看到 my-cli
项目已经出如今 npm 全局安装路径中,hi
命令指向的正是项目中的 index.js
文件。来验证下 hi
命令:
hi
命令生效了。而且因为使用的是 npm link
方式,因此当你修改 index.js
文件时,是能够同步更新 hi
命令执行结果的。到这里,脚手架的第一步已经完成,接下来就是拓展命令操做而已,其实本质上就是在 index.js
文件中编写 nodejs 代码。
hi init
命令咱们能够尝试拓展一个 init
命令,这里只写思路,不涉及特别复杂的 init 内容。
当咱们在终端输入 hi init
,但愿可以识别到 init
参数,并执行对应逻辑。在 nodejs 中,可使用 process
模块中的 argv
参数获取命令行参数,将 my-cli
项目中的 index.js
文件改成以下内容:
#! /usr/bin/env node
const process = require('process')
console.log('argv=>>',process.argv)
复制代码
而后在终端执行 hi init
:
能够看到 process.argv
数组的第三项开始就是咱们在 hi
命令后输入的参数,这样就好办了,咱们再改下代码:
#! /usr/bin/env node
const process = require('process')
if(process.argv[2] === 'init'){
init()
}
function init(){
console.log('start init')
}
复制代码
此时在终端执行 hi init
:
init
命令生效,实际开发中就能够写入对应逻辑,这里再也不扩展说明了。
真实脚手架开发中,须要定义的操做命令可能会有不少,就如 vue-cli
那样,此时须要借助插件来解析参数,若是不用插件,就须要本身一个个来判断所输入的参数,影响效率且容易出错。这里推荐 commander 插件,具体使用方法请参考文档,我这里列举一个使用插件的小例子:
#! /usr/bin/env node
const process = require('process')
const {program} = require('commander')
program
.version('0.0.1')
.option('-i, --init','用来初始化项目的')
.option('-r, --remove','用来删除的')
.parse(process.argv)
const options = program.opts()
if(options.init){
console.log('正在执行初始化操做');
}
if(options.remove){
console.log('正在执行删除操做');
}
复制代码
此时在终端输入对应命令的效果:
是否是像那么回事了。
以上就是一个前端脚手架开发的基本原理,了解了这些基础,就能够慢慢开发一个本身的脚手架了。固然开发一个能用于生产的脚手架不是像写 demo 那样简单的,须要学习的还有不少。以后有时间我会继续记录开发脚手架的相关文章,共同窗习!