脚手架架构设计和框架搭建
将收获什么
- 脚手架的实现原理
Lerna的常用方法- 架构设计技巧和架构图绘制方法
主要内容
- 学习如何以架构师的角度思考基础架构问题
- 多
Package项目管理痛点和解决方案,基于Lerna脚手架框架搭建 - 脚手架需求分析和架构设计,架构设计图
- 脚手架调试技巧
Lerna源码分析node的module模块分析yargs使用方法- 剖析
Lerna架构设计
开发脚手架的必要性
开发脚手架的核心目标是:提升前端研发效能
以下内容如果全靠脚手架进行自动化处理,可以提高相当大的研发效率了。
创建项目 + 通用代码
- 埋点
http请求- 工具方法
- 组件库
git 操作
- 创建仓库
- 代码冲突
- 远程代码同步
- 创建版本
- 发布打
tag
构建 + 发布上线
- 依赖安装和构建
- 资源上传
cdn - 域名绑定
- 测试/正式服务器
脚手架核心价值
将研发过程
- 自动化:项目重复代码拷贝、
git操作、发布上线操作 - 标准化:项目创建、git flow、发布流程、回滚流程
- 数据化:研发过程系统化、数据化、使得研发过程可量化
和自动化构建工具的区别
问题:jenkins,travis 等自动化构建工具已经很成熟了,为什么还要自研脚手架?
- 不满足需求:
jenkins,travis通常在git hooks中触发,需要在服务端执行,无法覆盖研发人员本地的功能,如:创建项目自动化,本地git操作自动化等。 - 定制复杂:
jenkins,travis定制过程需要开发插件,其过程较为复杂,需要使用java语言,对前端同学不太友好。
什么是脚手架
脚手架本质是一个操作系统的客户端,他通过命令行执行,比如
vue create vue-test-app
上面这条命令由 3 个部分组成:
- 主命令:
vue - command:
create - command 的 param:
vue-test-app
他表示创建一个 vue 项目,项目的名称为 vue-test-app,这是一个比较简单的脚手架命令,但实际场景往往更加复杂,比如:
当前目录已经有文件了,我们需要覆盖当前目录的文件,强制进行安装 vue 项目,此时我们就可以输入
vue create vue-test-app --force
这里的 --force 叫做 option ,用来辅助脚手架确认在特定场景下用户的选择(可以理解为配置)。还有一种场景:
通过 vue create 创建项目时,会自动执行 npm install 帮助用户安装依赖,如果我们希望使用淘宝源来安装,可以输入命令
vue create vue-test-app --force -r https://registry.npm.taobao.org
这里的 -r 也叫做 option,它与 --force 不同的是它使用 - ,并且使用简写,这里的 -r 也可以替换成 --registry,输入下面的命令就可以看到 vue create 支持的所有 options。
vue create --helps
-r 后面的 https://registry.npm.taobao.org 成为 option 的 param ,其实 --force 可以理解为:--force true ,简写为 --force 或 -f 。
脚手架的执行原理
脚手架执行原理如下
- 在终端输入
vue create project - 终端解析出
vue - 在环境变量中通过
which vue找到vue命令, 目录所在/node/bin/vue,所以我们执行的vue,实际上运行的是/node/bin/vue的这个vue - 这个
vue只是一个链接,终端根据vue命令链接到实际文件/node/lib/node_modules/@vue/cli/bin/vue.js - 终端利用
node执行vue.js vue.js解析command以及paramvue.js执行command- 执行完毕,退出执行
如何开发一个脚手架
以 vue-cli 为例
- 开发一个
npm项目,该项目中应包含一个bin/vue.js文件,并将这个项目发布到npm; - 将这个项目发布到
npm - 将
npm项目上的项目全局安装到node的lib/node_modules - 在
node的bin目录下配置vue软链接指向lib/node_modules/@vue/cli/bin/vue.js
这样我们在执行 vue 命令的时候就可以找到 vue.js 进行相关操作。
脚手架实现原理问题
为什么全局安装 @vue/cli 后会添加一个 vue 的命令呢?
npm i -g @vue/cli
运行 vue 命令时,实际走的是 node/bin/vue ,而这个文件只是一个软连接,指向lib/node_modules/@vue/cli/bin/vue.js。
回到上级目录 lib/node_modules/@vue/cli,打开
package.json 文件,里面的 bin 字段定义了这样的绑定关系。
// lib/node_modules/@vue/cli/package.json
{
"bin": {
"vue": "bin/vue.js"
},
}
总结:执行 vue 命令的时候,启动的是 bin/vue 这个文件,而这个文件指向lib/node_modules/@vue/cli/bin/vue.js ,所以最终启动的是 lib/node_modules/@vue/cli/bin/vue.js
全局安装 @vue/cli 的时候发生了什么?
-
把
@vue/cli的包通过npm安装到node/lib/node_modules这个目录下。 -
解析
package.json文件 ,根据文件中的bin字段,在/node/bin目录下创建软连接,软连接指向bin字段中规定的文件,也就是lib/node_modules/@vue/cli/bin/vue.js。
执行 vue 命令时发生了什么?
- 根据
which vue这条指令(在环境变量中查找),找到vue命令所在文件 - 运行这个文件,执行
vue和执行node/bin/vue的结果是一样的 - 根据软连接,执行真实的
lib/node_modules/@vue/cli/bin/vue.js文件
为什么 vue 指向一个 js 文件,我们却可以直接通过 vue 命令去执行它?
查看 lib/node_modules/@vue/cli/bin/vue.js 文件的源码,会发现第一行代码是这样的
#!/usr/bin/env node
它的意思就是在环境变量中查找使用 node 命令来运行此文件。
为什么说脚手架本质是操作系统的客户端?
因为 node 本身是一个客户端,在 windows 系统下,可以看到 node 的安装目录中,node 是以 node.exe 的形式出现的。
而我们编写的脚手架文件,如 vue.js 只是 node 运行时的一个参数。
node vue.js
如何为 node 脚手架创建别名?
软连接是可以嵌套的,只需让别名指向原来的名字即可。
脚手架执行的全过程
脚手架开发流程
开发流程
- 创建
npm项目 - 创建脚手架入口文件,最上方添加
#!/usr/bin/env node - 配置
package.json文件,添加bin属性,指定脚手架名称和入口文件地址 - 编写脚手架代码
- 将脚手架发布到
npm
使用流程
- 安装脚手架
npm i -g @vue/cli
- 使用脚手架
vue create project
脚手架开发难点
-
分包:将复杂的系统拆分成多个模块
-
命令注册
-
参数解析
-
帮助文档
-
命令行交互
-
日志打印
-
命令行文字变色
-
网络通信:
HTTP/WebSocket -
文件处理
-
... ...
开发一个简单的脚手架
- 新建文件夹
test-cli
mkdir test-cli
- 进入到
test-cli,- 初始化npm包,通过code .可以快速使用vscode打开当前文件夹。
cd test-cli
npm init -y
code .
- 添加
bin/index.js文件,内容如下
// bin/index.js
#!/usr/bin/env node
console.log('🚀🚀~ 脚手架开发 测试');
-
修改
package.json文件,添加bin属性,指定脚手架名称和入口文件地址
// package.json
{
"name": "test-cli-0174",
"version": "1.0.0",
"description": "",
"bin": {
"test-cli": "bin/index.js"
},
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
- 发布包到
npm
-
全局安装
npm i -g test-cli
- 命令行执行命令
test-cli
结果如下,控制台输出 🚀🚀~ 脚手架开发 测试
调试本地脚手架
进入到 test-cli 目录中
先全局移除之前通过 npm 安装的包,然后执行 npm link
npm remove test-cli -g
npm link
就会安装本地的脚手架了
随便修改本地代码后,然后再通过命令 test-cli 去启动脚手架
如果工程很复杂需要分包
在 test-cli 目录同级新建一个 test-cli-lib 目录,同样进行初始化。
npm init -y
然后新建 lib/index.js 文件,写上一个方法。
// lib/index.js
module.exports = {
sum(a, b) {
return a + b
}
}
修改 package.json 文件中的 main 属性
"main": "lib/index.js",
进入 test-cli-lib 目录 , 执行 npm link ,把这个包也安装到本地。
返回 test-cli 目录, 执行
npm link test-cli-lib
然后手动的修改 package.json 文件中的 dependencies 属性
"dependencies": {
"test-cli-lib"
}
就可以把这个包连接起来了,然后再修改本地代码测试一下。
// test-cli/bin/index.js
#!/usr/bin/env node
const lib = require("test-cli-0174-lib")
console.log(lib);
console.log('🚀🚀~ 本地脚手架开发 测试!!!!');
运行 test-cli 命令
可以把函数正常的打印出来了。
注意:当开发完成后需要发布到 npm 上,然后通过 npm 安装的时候,需要执行
npm unlink test-cli
npm unlink test-cli-lib
npm remove -g test-cli
npm remove -g test-cli-lib
然后再通过 npm 安装就行了
npm i -g test-cli
npm i -g test-cli-lib