这篇文章主要会对 vue-cli 工具源码进行一些解读及学习,版本为 4.0.0-beta1 。
项目结构
lerna
由于整个 vue-cli 中包含了多个项目,比如 cli 对应的 babel, eslint, pwa, ts 等插件,兼容 2.x 版本初始化项目的 cli-init, 启动本地服务的 cli-service 等等,管理多个包项目及发布这里就使用了 lerna 。
Lerna is a tool that optimizes the workflow around managing multi-package repositories with git and npm.
lerna 作为一个多项目工作流管理工具,支持一些项目的初始化配置工作,并且支持项目中每个包的整体或者独立发布。关于更多 lerna 的介绍和使用,可以参考官方文档以及知乎阿里云中台专栏的使用lerna优雅地管理多个package 。
packages

如上图所示,大部分的工具都集中在 /packages/@vue 下,大概可以分为以下几类:
- commands
cli 以及 cli-init 中承担了大部分 command 的实现,如何初始化项目,添加插件,启动服务,打包,启动 ui 界面等等,其中一部分是在 cli/lib 中有实现,也有一部分单独抽象成了其他的工具在 @vue 下的其他包里。
- plugins
上面有提到,有很多辅助的 plugin 。
- service
主要是 cli-service 以及 cli-service-global 。负责启动本地服务展示页面,基于 webpack-dev-server 实现,里面也有很多 webpack 的打包配置工作,这里再单独说下,vue-cli 2.x 版本时,初始化项目会把 webpack 配置信息暴露到项目中的 build 以及 config 目录中,而到 3.x 后会隐藏 webpack 配置,如果想单独配置一些打包信息可以新建一个 config 文件,这点与 create-react-app 很像,不过不支持 create-react-app 的 eject 操作来将 webpack 配置弹出到上层。
- ui
ui 模块也是 vue-cli 3.x 主推的新功能,通过可视化界面来管理,维护,检测项目状态。
它会有一个全局的项目管理器支持浏览项目,创建及导入:

进入到项目中则可以看到 dashboard,插件,依赖,配置和任务管理。
- utils
cli-shared-utils 和 cli-test-utils 主要是共用的一些工具方法。
Scripts
项目中有很多工具脚本,存放在 scripts 目录下,
- bootstrap
负责给新创建的包做些初始化工作,比如添加 package.json, README.md 和 .npmignore 文件。
- buildEditorConfig
创建一个 .editorconfig 文件,里面配置了一些规则比如换行方式,缩进,行最大长度等等。
- checkLinks
检查远程链接的网络访问。
- genChangeLog, genDocs
生成 change log 以及文档。
- patchChromedriver
Appveyor 只支持 Chrome 72 以上,所以这里在 package.json 配置下 chromedriver 版本号。
- release, syncDeps
选择发布类型,版本号,同步文件(通过 syncDeps)然后执行 lerna publish 流程。
- test, testSetup
测试环境配置及使用 jest 测试
- verifyCommitMsg
检查提交 msg, 对文案进行正则检查(版本号及文案开头的提交标识), 不过检查失败的文案貌似不太正确,比如提示说运行 npm run commit 来生成一个提交消息,不过并没有这个脚本。
Commands
命令行接口都在 @vue/cli/bin/vue.js 下,关系如下图所示:

create
@vue/cli/lib/create.js 会进行一层参数检查,错误处理,实际工作会交给同目录下的 Creator.js 去处理,最后一个参数则是需要提示交互的模块,内置的模块列表包括 babel, typescript, pwa, router, vuex, cssPreprocessors, linter, unit, e2e 。
Creator 继承自 EventEmitter, 方便在创建过程中发射一些事件, create 函数则会根据参数来执行创建流程:
- 是否读取 preset 配置 (远程或存储在本地)
- 注入服务
- 一些 legacy support 比如 router, vuex
- 创建 package manager,优先 yarn
- 检查 cli 版本
- 生成 package 对象并将它写入 package.json 文件中
- 根据配置来决定是否初始化 git
- 执行 install 安装依赖 (后面也会在有些步骤后执行 install,不再重复)
- 实例化 Generator 对象并生成相关文件
- 执行完成后的回调
- 生成并写入 README 文件
- 执行 git commit 完成变更提交
- 写 log, 整个流程结束
add & invoke plugin
add 会检查 git 是否有未提交的 change, 然后也借助 package manager 添加和安装插件,最后 invoke 这个插件。
invoke 的步骤则相对多一点:
- 检查 git 是否有未提交的 change (同 add)
- 定位这个插件是否在 pakcage.json 中
- 加载这个插件的生成器
- 解析 options, 如果包含 prompt 则提示
- 生成 plugin 对象然后运行生成器
const plugin = {
id,
apply: pluginGenerator,
options: {
registry,
...pluginOptions
}
}
await runGenerator(context, plugin, pkg)
这里的生成器也是 create 中的 Generator。
inspect
inspect 命令用来检查 webpack 的配置,借助 vue-cli-service 完成。
对应命令的实现在 cli-service/lib/commands/inspect.js 中,具体步骤不是很复杂,把所有的 webpack rules, loaders, plugins dump 出来。
在 npx vue-cli-service inspect --mode production
执行后,我们就能拿到 production 环境的 webpack 配置,cli 3.x 使用的是 webpack 4 (5 貌似快发布了),legacy 的 init 则使用的 webpack 3,对比一下两个 cli 版本的配置大部分比较相同,部分的写法不太相同比如 code split 之前使用的是 CommonChunkPlugin, 现在则是 optimization
里单独配置如下:
splitChunks: {
cacheGroups: {
vendors: {
name: 'chunk-vendors',
test: /[\\\/]node_modules[\\\/]/,
priority: -10,
chunks: 'initial'
},
common: {
name: 'chunk-common',
minChunks: 2,
priority: -20,
chunks: 'initial',
reuseExistingChunk: true
}
}
}
rules 则是增加了一些文件的支持比如 .pug, .stylus, .less 等(之前 .vue 文件里也支持 less 以及 stylus,但是不支持单独的文件 loader,需要手工配置)。plugins 里增加了 VueLoaderPlugin, OptimizeCssnanoPlugin, PreloadPlugin 等等。
serve
serve 命令对应在 cli-service/lib/commands/inspect.js 中,server 部分是基于 webpack-dev-server, 启动的步骤大概如下:
- 对 webpack 进行配置,比如 source-map, hmr, progress plugin
- 解析和校验 webpack 配置
- 暴露状态,入口设置
- 解析 server 的选项,比如是否使用 https, host 和 port 配置,代理信息等
- 如果不是生产环境,使用 hot-reload 中间件
- 构造 server 对象,使用的参数大部分都是之前步骤获取到的,实例化代码如下图所示:

- return 一个 promise,等待 webpack 编译完成
- 完成后会把 url 写到剪贴板中,并判断是不是第一次编译,如果是则打开浏览器并加载该 url, 如果有 dashboard,还会通过 ipc manager 发送过去
- 启动 server,监听指定端口
build
build 的实现在 cli-service/lib/commands/build 目录中,会先确保兼容性区分是不是 legacy build 。然后才开始构建流程:
- 解析目标地址
- 解析 webpack 的配置并验证
- 如果参数中有 dashboard, 借助 DashboardPlugin 把状态暴露出去
- 如果参数中有 report, 会借助 BundleAnalyzerPlugin 来生成包依赖的报告
- 执行 webpack 编译
- 完成后检查是否有错误,并输出一些信息到控制台
ui
ui 部分代码则会多一些,分为前后端两部分。使用的技术栈如下
Front-end:
- js framework: vue
- router: vue-router
- i18n: vue-i18n
- api: graphql
Server (appollo):
- node framework: express
- static serve: express.static
- api: graphql
- database: lowdb
- cache: lru-cache
- ...
vue 跟 express 已经很常见了,lowdb 之前没有接触过,是一个基于 node 的纯 json 文件轻量级本地数据库,直接使用一个 json 文件来存储一些需要本地持久化的信息。
其他
还有一些其他的命令比如 init, config, upgrade, info.
- init 直接调用的 2.9.2 版本的 vue-cli 的 init 命令。
- config 系统根目录下会有 .vuerc 文件,有一些简单的配置,这里支持 set, get 以及 delete.
- upgrade 支持整体 upgrade 或者单个 package, 会有一些 migrate 的逻辑。
- info 通过 envinfo 包来输出一些系统信息到控制台。
到这里几乎对 4.0 beta Vue-Cli 所有的命令及相关工具都进行了简单的解读,欢迎提出建议和指正。