从使用角度理解什么是脚手架
- 脚手架本质是一个操作系统的客户端
- 在终端中去执行一个命令,这个命令本身它就是一个客户端
- 我们其实可以把脚手架理解为操作系统的一个客户端
- 通过命令去执行它的时候,这个命令往往是这样的一个构造,如下
- 比如:要创建一个vue的项目的时候, $
vue create vue-test-app
- 上面这条命令由3个部分组成
- 主命令: vue
- command: create
- 这个 command 是子命令,实际上它向脚手架(主命令)发送一个请求
- 这个请求,让我们的脚手架帮我们完成一个动作,完成这个动作,就是create创建项目
- command的param: vue-test-app
- 建什么项目呢?看这第三个参数,在command后面,又加了一个空格,输入了一个参数
- 这个命令输入完以后,脚手架会给我们一定的反馈, 比如说会让我们做一些选择,或者会帮我们执行一些操作等等
- 它表示创建一个vue项目,项目名称为 vue-test-app
- 上面这条命令由3个部分组成
- 以上是一个较为简单的脚手架命令,但实际场景往往更复杂,比如:
- 当前目录已经有文件了,我们需要覆盖当前目录下的文件,强制进行安装 vue 项目,此时我们就可以输入
- $
vue create vue-test-app --force
--force
叫做 option, 用来辅助脚手架确认在特定场景下用户的选择(可以理解为配置)- 如果加入了 --force,就等于告诉脚手架创建项目的时候,可以强制进行覆盖
- 如果没加 --force, 那么脚手架的执行就会中断。因为你没有强制覆盖,而我当前又有文件,所以我就会中断
- 其实
--force
可以理解为--force true
, 简写为:--force
或-f
- 还有一种场景, 通过
vue create
创建项目时,会自动执行npm install
帮用户安装依赖- 如果我们使用淘宝源来安装,就可以输入命令
vue create vue-test-app --force -r https://registry.npm.taobao.org
- 这里的
-r
也是 option, 它与--force
不同的是它使用-
,并且使用简写 - 这里
-r
也可替换成--registry
,同时,我们可以通过vue create --help
来查看其他可用命令 -r https://registry.npm.taobao.org
后面的https://registry.npm.taobao.org
成为 option的param
- 如果我们使用淘宝源来安装,就可以输入命令
脚手架原理
-
我们需要理解输入了 $
vue create vue-test-app
之后,为什么会发生一系列的事情 -
我们先要理解整个 vue 脚手架的体系,如下图
- 通过 $
which vue
可看到/usr/local/bin/vue
, 这里的vue其实是一个软连接- 参考:vue -> …/…/…/Users/xxx/.config/yarn/global/node_modules/.bin/vue
- 前面的 vue 其实执行的就是后面路径里的vue命令
- 程序员在终端中输入 $
vue create vue-test-app
- 终端解析出 vue 命令
- 终端在环境变量中找到 vue 命令
- 终端根据 vue 命令链接到实际文件 vue.js
- 终端利用 node 执行 vue.js
- vue.js 解析 commond / options
- vue.js 执行 commond
- 执行完毕,退出
从应用角度开发
-
以 vue-cli 为例开发一个脚手架
- 比如:/Users/johnny/.config/yarn/global/node_modules/@vue/cli
-
其目录结构
LICENSE README.md bin lib node_modules package.json
-
需要开发一个 npm 项目,该项目中应包含一个 bin/vue.js 文件,并将这个项目发布到 npm
-
将 npm 项目安装到 node 的 lib/node_modules,或者如: /…/yarn/global/node_modules
-
在 node 的 bin 目录下配置 vue 软链接指向 lib/node_modules
-
在执行vue命令的时候,就可以找到 vue.js 进行执行,这样一个板顶关系,在package.json中
{ "bin": { "vue": "bin/vue.js" } }
-
这个就是 vue 和 vue.js 之间的绑定关系
-
这就是为什么全局安装 @vue/cli 后会添加的命令为 vue
-
$
npm i -g @vue/cli
全局安装 @vue/cli 时发生了什么?- 在安装时,会把当前npm包下载到 lib/node_modules
- 或者如: /…/yarn/global/node_modules 下
- 之后解析 package.json 中的 bin 属性
- 创建一个软连接
/usr/local/bin/vue
指向/../yarn/global/node_modules/@vue/cli/bin/vue.js
中
- 在安装时,会把当前npm包下载到 lib/node_modules
-
执行vue命令时,发生了什么?为何 vue 指向了 js 文件,却可以通过 vue 命令执行它
- 操作系统,会根据 $
which vue
中的 vue 路径找到 vue执行文件,如果 vue 命令不存在,会报出 command not found 的提示 - 这个vue执行路径其实是一个软链接,通过这个软连接找到真实的 vue地址,对应的就是 上述 /…/bin/vue.js
- 这个 vue.js 是一个 js文件,即使有执行权限,也不可直接执行,需要一个 node 的解释器,就是类似 node /…/bin/vue.js
- 但是很明显,在执行vue命令时,并没有冠以 node 的前缀,直接进入 /…/yarn/global/node_modules/@vue/cli/bin/vue.js
- 可看到,最顶部有一个声明
#!/usr/bin/env node
这个声明可以直接让 xx.js 文件直接在操作系统执行 - 意思是,告诉操作系统,在直接调用这个文件的时候,到环境变量中去找 node 命令,类似的,如果是个python文件,将node修改成 python
/usr/bin/env
会列出所有环境变量,在这个环境变量列表中找到 node 命令- 通过 node 命令来执行这个 js 文件
- 因此,任何js文件,只要在文件顶部加上这个声明,即可 直接执行 (这里限制在 unix 系统)
- 此时, ./xx.js 等价于 node xx.js
- 注意,
#!/usr/bin/env node
这样写的好处是- 无需关注每一个电脑环境下的node路径
- 这个也可替换成具体的node路径也是一样的,但是会非常的麻烦,而且换个环境可能就不能用了
- 所以,还是采用这种方式来处理
- 操作系统,会根据 $
-
现在我们想要用
mycommand
(这个是我们自定义脚手架随意举例) 指向 xx.js,就可以通过创建软链接的方式来处理- 进入到 /usr/local/bin 目录
- 创建软链接 $
ln -s /../xx.js mycommand
- 注意,这里的 /…/xx.js 是当前脚本的路径,替换成自己的即可
- 可看到当前目录下有一个 imooc 的文件
- 此时,在系统内任意位置,只需要执行 mycommand 即可运行 xx.js
-
这个就是我们自定义的脚手架 mycommand 的基本设定
最后,描述一下脚手架命令执行的一个整个过程
脚手架的本质是操作系统上的一个客户端
- 可以发现脚手架执行起来的过程和前端开发的应用是非常不一样的
- 其实它和web应用从本质上来讲,没有关系
- 它们的联系仅仅是脚手架和开发的web应用都是通过 js 这个语言来进行编写的
- 但其实脚手架它其实是操作系统当中的一种机制
- 执行脚手架的时候,它其实是被作为一个操作系统上的可执行文件来进行执行的
- 所以可以说我们开发的脚手架就是操作系统上的一个客户端
- 我们的脚手架它本质执行起来时候,是依靠 node 这个命令
- node 是一个操作系统客户端,而我们的 xx.js 仅仅是node的一个参数
- 换句话说,仅仅是把一个参数注到 node 当中,类似的可以这样 node -e console.log(1) 这样也可以执行
- 也就是说脚手架是操作系统的客户端,其本质并不是说 xx.js 是一个客户端,而是node本身它是一个客户端
- 在windows中,node 是 node.exe
- 在mac中,node 是 node* (*代表可执行文件)
- 当去执行 node xx.js 的时候,其实是把 xx.js 中的代码变成一个字符串传入到node中
- 然后 node 对这些字符串进行解析,把它当成一个可执行程序进行执行
- 这些逻辑全部是预设好的,预设好的逻辑就存放在这个node的可执行文件当中
- 所以脚手架是客户端,其本质是因为node本身就是客户端
脚手架和在PC上面安装的其他应用或者软件的区别
- 本质来说没有区别,在操作系统上安装了软件,windows上都是 exe 后缀?
- 在 macos 上面都是可执行文件,所以本质没有区别
- 它们的区别仅仅是安装的一些软件会提供一个GUI,像当前终端一样,它会提供一个GUI
- 而我们的 node 并没有提供 GUI
- 我们的node是直接通过命令行的方式传入参数来进行执行,仅仅是这样的一个差别
- 如果想在 nodejs 当中去展示GUI有可能吗?
- 完全可能我们只需要去调用操作系统的GUI绘制API就可以绘制出窗口来
- 所以脚手架本质它其实就是个客户端。但注意, 不是我们编写的代码是客户端,而是node本身是客户端
为 node 脚手架创建别名
- 方式 1,就是通过编写一个软链接,其本身会链接到我们自己编写的一个代码文件上
- 方式 2,现在继续想给 mycommand 添加一个别名或者对其进行修改
- 进入到 /usr/local/bin 目录
- 执行 ln -s ./mycommand mycommand1 这样就可以通过 mycommand1 来执行
- 可见,软链接是可以嵌套的,基于此可以给当前脚手架起别名