【架构师(第二篇)】脚手架架构设计和框架搭建

2022-12-10 13:20:19 浏览数 (1)


脚手架架构设计和框架搭建

将收获什么

  • 脚手架的实现原理
  • Lerna 的常用方法
  • 架构设计技巧和架构图绘制方法

主要内容

  • 学习如何以架构师的角度思考基础架构问题
  • Package 项目管理痛点和解决方案,基于 Lerna 脚手架框架搭建
  • 脚手架需求分析和架构设计,架构设计图
  • 脚手架调试技巧
  • Lerna 源码分析
  • nodemodule 模块分析
  • yargs 使用方法
  • 剖析 Lerna 架构设计

开发脚手架的必要性

开发脚手架的核心目标是:提升前端研发效能

以下内容如果全靠脚手架进行自动化处理,可以提高相当大的研发效率了。

创建项目 通用代码

  • 埋点
  • http 请求
  • 工具方法
  • 组件库

git 操作

  • 创建仓库
  • 代码冲突
  • 远程代码同步
  • 创建版本
  • 发布打 tag

构建 发布上线

  • 依赖安装和构建
  • 资源上传 cdn
  • 域名绑定
  • 测试/正式服务器

脚手架核心价值

将研发过程

  • 自动化:项目重复代码拷贝、git 操作、发布上线操作
  • 标准化:项目创建、git flow、发布流程、回滚流程
  • 数据化:研发过程系统化、数据化、使得研发过程可量化

和自动化构建工具的区别

问题:jenkinstravis 等自动化构建工具已经很成熟了,为什么还要自研脚手架?

  • 不满足需求:jenkinstravis 通常在 git hooks 中触发,需要在服务端执行,无法覆盖研发人员本地的功能,如:创建项目自动化,本地 git 操作自动化等。
  • 定制复杂: jenkinstravis 定制过程需要开发插件,其过程较为复杂,需要使用 java 语言,对前端同学不太友好。

什么是脚手架

脚手架本质是一个操作系统的客户端,他通过命令行执行,比如

代码语言:javascript复制
vue create vue-test-app

上面这条命令由 3 个部分组成:

  • 主命令vue
  • commandcreate
  • command 的 paramvue-test-app

他表示创建一个 vue 项目,项目的名称为 vue-test-app,这是一个比较简单的脚手架命令,但实际场景往往更加复杂,比如:

当前目录已经有文件了,我们需要覆盖当前目录的文件,强制进行安装 vue 项目,此时我们就可以输入

代码语言:javascript复制
vue create vue-test-app --force

这里的 --force 叫做 option ,用来辅助脚手架确认在特定场景下用户的选择(可以理解为配置)。还有一种场景:

通过 vue create 创建项目时,会自动执行 npm install 帮助用户安装依赖,如果我们希望使用淘宝源来安装,可以输入命令

代码语言:javascript复制
vue create vue-test-app --force -r https://registry.npm.taobao.org

这里的 -r 也叫做 option,它与 --force 不同的是它使用 - ,并且使用简写,这里的 -r 也可以替换成 --registry,输入下面的命令就可以看到 vue create 支持的所有 options

代码语言:javascript复制
vue create --helps

-r 后面的 https://registry.npm.taobao.org 成为 optionparam ,其实 --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 以及 param
  • vue.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 的命令呢?

代码语言:javascript复制
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 字段定义了这样的绑定关系。

代码语言:javascript复制
// 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 文件的源码,会发现第一行代码是这样的

代码语言:javascript复制
#!/usr/bin/env node

它的意思就是在环境变量中查找使用 node 命令来运行此文件。

为什么说脚手架本质是操作系统的客户端?

因为 node 本身是一个客户端,在 windows 系统下,可以看到 node 的安装目录中,node 是以 node.exe 的形式出现的。

而我们编写的脚手架文件,如 vue.js 只是 node 运行时的一个参数。

代码语言:javascript复制
node vue.js

如何为 node 脚手架创建别名?

软连接是可以嵌套的,只需让别名指向原来的名字即可。

脚手架执行的全过程

脚手架开发流程

开发流程

  • 创建 npm 项目
  • 创建脚手架入口文件,最上方添加 #!/usr/bin/env node
  • 配置 package.json 文件,添加 bin 属性,指定脚手架名称和入口文件地址
  • 编写脚手架代码
  • 将脚手架发布到 npm

使用流程

  • 安装脚手架
代码语言:javascript复制
npm i -g @vue/cli
  • 使用脚手架
代码语言:javascript复制
vue create project

脚手架开发难点

  • 分包:将复杂的系统拆分成多个模块
  • 命令注册
  • 参数解析
  • 帮助文档
  • 命令行交互
  • 日志打印
  • 命令行文字变色
  • 网络通信:HTTP/WebSocket
  • 文件处理
  • ... ...

开发一个简单的脚手架

  • 新建文件夹 test-cli
代码语言:javascript复制
mkdir test-cli
  • 进入到 test-cli ,- 初始化 npm 包,通过 code . 可以快速使用 vscode 打开当前文件夹。
代码语言:javascript复制
cd test-cli
npm init -y
code .
  • 添加 bin/index.js 文件,内容如下
代码语言:javascript复制
// bin/index.js

#!/usr/bin/env node

console.log('


	

0 人点赞