脚手架

165 阅读5分钟

一、认识脚手架

1. 为什么要开发脚手架

前端开发现状

  1. 创建项目 & 通用代码:埋点、HTTP 请求、工具方法、组件库
  2. git 操作:创建仓库、代码冲突、远程代码同步、创建版本、发布打 tag
  3. 发布上线:依赖安装和构建、资源上传CDN、域名绑定、测试/正式服务器

核心价值:

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

jenkins、travis等自动化构建工具已经比较成熟了,为什么还需要自研脚手架?

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

2. 从使用的角度理解什么是脚手架

创建项目

  • 标准模版创建
  • 自定义规则创建
  • 创建组件库
  • 自动安装和启动

发布项目

  • Git 自动化
  • 云构建
  • 项目自动发布
  • 组件自动发布

3. 脚手架的执行原理 - 以 vue 为例

  • 在终端输入 vue create vue-test-app
  • 终端解析出 vue 命令
  • 终端在环境变量中找到 vue 命令
  • 终端根据 vue 命令链接到实际文件 vue.js
  • 终端利用 node 执行 vue.js
  • vue.js 解析 command / options
  • vue.js 执行 command
  • 执行完毕,退出执行

4. 从应用的角度看如何开发一个脚手架(以 vue-cli 为例):

  • 开发 npm 项目,项目中要包含一个 bin/vue.js 文件,并将这个项目发布到 npm
  • 将 npm 项目安装到 node 的 lib/node_modules
  • 在 node 的 bin 目录下配置 vue 软链接指向 lib/node_modules/@vue/cli/bin/vue.js

这样在执行 vue 命令的时候就可以找到 vue.js 并执行

疑问:

  1. 为什么全局安装 @vue/cli 后会添加的命令为 vue ? vue cli 下面的 package.jspn 里面的 bin 指定了软链接 vue: 'bin/vue.js'
  2. 全局安装 @vue/cli 时发生了什么?
    • 把依赖下载到指定 node_module 目录下面
    • 配置一个 bin 的软链接
  3. 执行 vue 命令的时候发生了什么?为什么 vue 指向一个 js 文件,我们却可以直接通过 vue 命令直接去执行它?
    • 在环境变量当中找 vue 命令有没有被注册
    • js 文件需要通过一个解释器进行执行 ---- node
    • vue.js 有一行 #!/usr/bin/env node -> 告诉操作系统,在直接调用这个文件的时候去环境变量中找 node 这个命令,通过 node 命令去执行

可以通过 echo $PATH 看到当前环境所有的环境变量

5. 脚手架原理进阶

  • 为什么说脚手架本质是操作系统的客户端?它和我们在PC上安装的应用/软件有什么区别?
    • 因为 node 本身是一个客户端,用 node 来执行,本质没有区别
  • 如何为 node 脚手架命令创建别名?
    • 在 bin 目录下创建软链接:ln -s /Users/fanzhang/Desktop/vue-test/test.js ice
    • 可以嵌套
  • 描述脚手架命令执行的全过程
    • 输入 vue create vue-test-app
    • 在环境变量 $PATH 中查询 vue 命令 --- 相当于 which vue
    • 查询实际链接文件
    • 通过 /usr/bin/env node 执行文件

6. 脚手架开发流程和难点解析

开发流程

  1. 创建 npm 项目
  2. 创建脚手架入口文件,最上方添加 #!/usr/bin/env node
  3. 配置 package.json ,添加 bin 属性
  4. 编写脚手架代码
  5. 将脚手架发布到 npm

使用流程

  1. 安装脚手架:npm install -g your-own-cli
  2. 使用脚手架:your-own-cli

难点解析

  • 分包:将复杂的系统拆分成若干个模块
  • 命令注册:create add
  • 参数解析:vue command [options]
  • options 全称 和 简写
  • 带 params 的 options:--path /users/Desktop/vue-test
  • 帮助文档
    • global help
      • Usage Options Commands
    • command help
      • Usage options
  • 其他
    • 命令行交互
    • 日志打印
    • 网络通信:HTTP/webSocket
    • ...

二、开发第一个脚手架

1. 新建脚手架

$ mkdir ice
$ cd ice
$ npm init
$ touch bin/index.js

bin/index.js

#!/usr/bin/env node

package.json 新增 bin 属性

ice: bin/index.js
$ npm login
$ npm publish

2. 本地安装脚手架

$ npm install -g ice

3. 本地调试

  • npm link ice --- (常用)
  • 在存在 ice 的文件夹下 npm install
  • 在环境变量中创建短链: ln -s 本地链接 ice

4. 脚手架本地 link 标准流程、分包

链接本地脚手架

$ cd your-link-dir
$ npm link

链接本地库文件

$ cd your-lib-dir
$ npm link
$ cd your-cli-dir
$ # link 存在
$ npm link your-lib-dir
$ # link 不存在
$ rm -rf node_modules
$ npm install

取消链接本地库文件

$ cd your-lib-dir
$ npm unlink
$ cd your-cli-dir
$ npm unlink your-lib-dir
  • 理解 npm link
    • npm link your-lib: 将当前项目中 node_modules 下指定的库文件链接到全局 node_modules 下的库文件
    • npm link:将当前项目链接到 node 全局 node_modules 中作为一个库文件,并解析 bin 配置创建可执行文件
  • 理解 npm unlink
    • npm unlink:将当前项目从 node 全局 node_modules 中移除
    • npm unlink your-lib:将当前项目中的库文件依赖移除

5. 脚手架命令注册和参数解析

6. 原生脚手架开发痛点分析

  1. 痛点一:重复操作
  • 多 package 本地 link
  • 多 package 依赖安装
  • 多 package 单元测试
  • 多 package 代码提交
  • 多 package 代码发布
  1. 痛点二:版本一致性
  • 发布后版本一致性
  • 发布后相互依赖版本升级

三、lerna

1. 认识 lerna

lerna 是一个优化基于 git+npm 的多 package 项目的管理工具

优势:

  • 大幅减少重复操作
  • 提升操作的标准化

lerna 是架构优化的产物,它揭示了一个架构真理:项目复杂度提升后,就需要对项目进行架构优化。 架构优化的主要目标往往都是以效能为核心的。

使用 Lerna 管理的大型项目:

  • babel
  • vue-cli
  • create-react-app

2. 基于lerna搭建脚手架框架

lerna 开发脚手架流程

  1. 脚手架项目初始化
    • 初始化 npm 项目
    • 安装 lerna
    • lerna init 初始化项目
  2. 创建 package --- 子项目
    • lerna create 创建 package
    • lerna add 安装依赖
    • lerna link 链接依赖
  3. 脚手架开发和测试
    • lerna exec 执行 shell 脚本
    • lerna run 执行 npm 命令
    • lerna clean 清空依赖
    • lerna bootstrap 重装依赖
  4. 脚手架发布上线
    • lerna version bump version
    • lerna changed 查看上版本以来的所有变更
    • lerna diff 查看diff
    • lerna publish 项目发布

2. lerna 源码分析

blog:fanfanyir.github.io/2022/03/27/…