从头开始构建你的开发生态系统:自定义脚手架的完整指南(一)

918 阅读7分钟

什么是脚手架

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

CLI,全称是 command-line interface,也就是命令行界面。

脚手架本质上就是一个操作系统的客户端,它通过命令行执行

其中大家最熟悉的就是 create-react-app 和 vue-cli,还有自研的tccli

比如 vue-cli

vue create vue-test

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

  • 主命令:vue
  • command(子命令):create
  • command 的 param:vue-test

它表示创建一个 vue 项目,项目名称为 vue-test,以上是最简单的脚手架命令,但实际场景往往更加复杂,比如: 当前项目已经有文件,我们需要覆盖当前目录下的文件,强制进行安装vue项目,此时我们就可以输入

vue create vue-test --force

这里的--force 叫做 option,用来辅助脚手架确认在特定场景下用户的选择(可以理解为配置)。还有一种场景:通过vue create创建项目时,会自动执行npm install帮助用户安装依赖,如果我们希望使用淘宝源来安装,可以输入命令:

vue create vue-test --force -r [https://registry.npm.taobao.org](https://registry.npm.taobao.org)

这里的 -r 也叫做 option,它与--force不同的是它使用 -,并且使用简写,这里的-r也可以替换成-- registry,我们可以通过帮助命令看到vue create支持的所有 options:

vue create --help

image.png

开发脚手架的必要性

往常项目开发时,如果新启一个项目,你需要从头配置很多东西,api,路由,权限,基础UI库,埋点等等初始化操作,为了简化和提高效率,才有了脚手架存在的意义

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

脚手架核心价值

整体来说就是将研发过程自动化,标准化

  • 减少重复性工作
  • 规范项目开发目录结构
  • 统一团队统一开发风格,便于跨团队合作,以及后期维护,降低新人上手成本
  • 提供一键前端项目的创建、配置、本地开发、插件扩展等功能,让开发者更多时间专注于业务

脚手架实现原理

首先抛出来三个问题

  1. 为什么全局安装@vue/cli 后为添加命令为vue?
  2. 全局安装@vue/cli时发生了什么 ?
  3. 执行vue命令的时候发生了什么?为什么vue指向一个js文件,我们却可以直接通过vue命令去执行它?

首先思考一下 当我们在终端输入vue create vue-test-app的时候,这个命令背后做了什么事情

简化版的思维导图

image.png

脚手架执行原理如下

  • 在终端输入 指令 vue create vue-test-app

  • 终端解析出vue命令

    • which 命令可以查看执行命令所在位置
  • 在环境变量中找到vue命令

  • 终端根据vue命令链接到实际vue.js文件

  • 终端利用node执行vue.js

  • vue.js解析command/options

  • vue.js执行command

  • 执行完毕,退出执行

第一个问题通过打开源码可以看到

image.png 配置了bin字段

许多软件包都具有一个或多个要安装到 PATH 中的可执行文件。

bin 字段是命令名到本地文件名的映射。在安装时,npm 会将文件符号链接到 prefix/bin 以进行全局安装或./node_modules/.bin/本地安装。 当我们使用 npm 或者 yarn 命令安装包时,如果该包的 package.json 文件有 bin 字段,就会在 node_modules 文件夹下面的 .bin 目录中复制了 bin 字段链接的执行文件。我们在调用执行文件时,可以不带路径,直接使用命令名来执行相对应的执行文件。

第二个问题

答案是: 下载依赖,并配置 bin 命令

#!/usr/bin/env node
#!/usr/bin/node

// 第一种是环境变量中查找node
// 第二种直接执行/usr/bin/目录下的node

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

是因为node 就是客户端,查看安装目录会发现 node是个.exe可执行文件, 执行vue命令 等价于 执行node vue , 可以看到vue是当作一个参数传入到node中的,然后执行预设程序. 这里脚手架可以理解为在客户端执行的可执行文件

image.png

实现一个自己的脚手架

脚手架的目的是为了在统一的标准下快速建设系统框架,把系统开发过程中需要的配置、组件、服务、测试,一并通过配置引入到系统开发中。

开发脚手架之前,我们要先自己理一下需求,要满足什么功能,有了方向和规划,才能更高效的去实现

在这里简单的理一下需求:

对用户而言(简化版):

image.png 对于CLI而言

image.png

开发流程

  • 创建npm项目

  • 创建脚手架入口文件,最上方添加:

    #!/usr/bin/env node

  • 配置package.json,添加bin属性

  • 编写脚手架代码

  • 将脚手架发布到 npm

npm包本地调试的两个办法

  • 在当前包的根目录下 npm install -g xxx 全局安装脚手架,系统会将命令软连接到当前文件
  • 使用 npm link 命令,会根据package.json上的配置,被链接到全局

npm发布的时候注意点

  • name不能重复,可以通过 npm view 包名查看是否已经有了,没有的话该命令会返回404

  • 解决重名烦恼也可以使用新建组织的方式, 但发布的时候需要 npm publish —access=publish当然 你是付费建立的那就不用了 常规的npm publish就可以

    • 或者在package.json中添加配置
"publishConfig": {"access": "publish"}

npm包本地调试的方法,链接本地库文件

// 进入你的本地库文件夹
cd your-lib-dir
//执行
npm link
// 到你使用这个库的项目内
cd your-cli-dir
// 执行
npm link you-lib-dir

取消链接本地库

// 进入你的本地库文件夹
cd your-lib-dir
//执行
npm unlink
// 到你使用这个库的项目内
cd your-cli-dir
// 执行
npm unlink you-lib-dir

原生脚手架开发痛点分析

  • 重复操作

    • 主要体现在多package方面,当一个脚手架包含多个package的时候, 多个包之间的本地link, 依赖安装,本地测试,代码提交与发布,都会产生大量的重复性操作.
  • 版本一致性问题

    • 发布的时候版本一致性
    • 发布的时候互相依赖包的版本升级,比如说a是公共工具包,它被b,c,d所使用,当a生版本后, bcd都需要更新版本时

开发中常用的依赖

  • babel-cli/babel-env:语法转换工具,有了它我们在开发脚手架的时候就可以使用 ES6 语法了
  • commander:命令行工具,有了它我们就可以读取命令行命令,知道用户想要做什么了
  • inquirer: 交互式命令行工具,给用户提供一个漂亮的界面和提出问题流的方式
  • download-git-repo:下载远程模板工具,负责下载远程仓库的模板项目
  • chalk:颜色插件,用来修改命令行输出样式,通过颜色区分 info、error 日志,清晰直观
  • ora:用于显示加载中的效果,类似于前端页面的 loading 效果,像下载模板这种耗时的操作,有了 loading 效果可以提示用户正在进行中,请耐心等待
  • log-symbols:日志彩色符号,用来显示√ 或 × 等的图标

此文章只是先讲了一下脚手架的概念,让大家对脚手架有个基本认知, 下一篇中,开始代码编写