文章目录
一、前言
最近抽时间研究了一下 Vue CLI,拜读了网络上不少大神的文章,学习到了一些看源码的方法和思路,着手看了 VUE CLI 最新的源码(当前版本是 5.0.4
),做了一些核心内容的源码分析。自己也试着搭了一个小型脚手架,有兴趣的可以移步到我的 github 账号。本文有借鉴参考以下几篇文章,特别标注以及鸣谢原创的作者们。
手把手从0到1实现一个web工程通用脚手架工具
从 0 构建自己的脚手架/CLI知识体系(万字)
现代 CLI 和 GUI 方案指南
Vue CLI 是如何实现的 – 终端命令行工具篇
前端进阶 从零搭建属于你的脚手架
download-git-repo
Vue CLI 源码在线阅读
二、Vue CLI 是什么?
官方的介绍如下,我们可以简单地把 Vue CLI 理解成一个脚手架。
2.1 什么是脚手架?
既是一个工具,也是一个前端工程化的解决方案。
一方面,能让我们快速地使用配置好的模板来初始化项目;另一方面,对前端项目生命周期,包括搭建,开发和部署,提供了一系列的工具和规范,让我们避免做重复工作,更专注与业务逻辑。
2.2 为什么要用脚手架?
前端项目初始化的发展历程
- 手动创建。通过手动创建
.html
,.css
,.js
文件,在.html
里引入.css
和.js
文件。 - 模板托管。将项目模板代码托管到
Git
/SVN
仓库上,使用的时候先手动将代码拉到本地,然后修改文件夹名以及项目中的配置。 - 自动化脚本。通过脚本,自动化拉取项目模板、配置项目信息等。
- 完整的脚手架。通过命令行
vue-cli
/create-react-app
初始化项目。 - 自定义扩展。在 Vue CLI 这种脚手架的基础上个性化定制,接入自定义模板、自动化 Git 流程等功能。让脚手架跳出命令行这种形式,通过可视化的交互,让一切变得更简单。
脚手架解决的核心问题
从这样一个发展历程可以看出,脚手架解决的核心问题就是 —— 帮助开发者更方便地初始化项目,这也就是为什么需要脚手架。
以前,初始化一个项目流程可能略微复杂,现在轻松搞掂;
与此同时,基于一套模板搭建多个系统,相同的项目结构利于后期维护;
最后,针对重复的工作实现自动化,如创建文件、配置项目信息…
如何实现一个脚手架?
脚手架工具一般要提供以下 4 个功能:
1.项目基本模板(可配置) – download-git-repo
+ Yeoman
2.自动化构建系统
3.模块打包(可配置)
4.项目代码规范
目前,常见的脚手架底层都会依赖两种方案:使用 download-git-repo
来下载模板代码,以及使用 Yeoman
来作为脚手架的一个核心能力进行扩展。通过 download-git-repo(opens new window)
这个 npm 包可以实现 git 仓库的拉取,支持 GitHub、GitLab 等。基于这个 npm 包,我们可以写一个简单的脚本来实现简易的模板下载功能。
download('direct:https://gitlab.com/flippidippi/download-git-repo-fixture.git', 'test/tmp', { clone: true }, function (err) {
console.log(err ? 'Error' : 'Success')
})
开发者们使用 Yeoman 提供的 API 可以定制任意脚手架,完全开放、自由的,扩展性极强。
可以理解 Yeoman 提供一个插件体系,开发者可以自定开发插件来使用 Yeoman 的能力,这个“插件”在 Yeoman 中称为 generators
,所有代码生成的能力都由 generators
提供。这个包的存在,给脚手架的实现提供了无限可能性。
常见脚手架集成方案
Vue CLI 和 Create React App。
● 共同点:提供了终端命令行工具、零配置脚手架、插件体系等
● 差异点:Vue CLI 提供了图形化界面对项目进行初始化、管理
三、Vue CLI 是如何实现的?
核心思想
核心思想主要有 2 个,分别是预设管理和灵活的插件系统。
预设管理
在 CLI 内部,通过统一的实例对 prompt
(终端交互)和 preset
(预设)进行管理。在初始化的时候,这些用于终端交互的配置就初始化在内存中,根据用户的选择,触发不同的逻辑。
插件系统
将 CLI 的逻辑和生成代码的逻辑 解耦,通过不同的插件去修改文件内容和配置,生成最终的代码。CLI 插件是向你的 Vue 项目提供可选功能的 npm 包,如 Babel/TypeScript 转译、ESLint 集成、单元测试等。当你在项目内部运行 vue-cli-service 命令时,它会自动解析并加载 package.json
中列出的所有 CLI 插件。
分析源码
围绕上面两个核心思想进行源码分析,源码中涉及到的核心类就是 Creator
和 Generator
。(下面这一块同时也是脚手架命令行的实现原理)
用法
从 Vue CLI 的用法入手,开始分析
yarn global add @vue/cli
vue create my-project
目前 Vue CLI 同时支持 Vue 2 和 Vue 3 项目的创建(默认配置)。
上面是 Vue CLI 提供的默认配置,可以快速地创建一个项目。除此之外,也可以根据自己的项目需求(是否使用 Babel、是否使用 TS 等)来自定义项目工程配置,这样会更加的灵活。
选择完成之后,就会开始执行安装依赖、拷贝模板等命令,直至项目初始化成功。
入口
这篇文章是基于 5.0.4 版本的 Vue CLI 进行分析。
在/packages/@vue/cli/package.json
中找到入口文件,如下:
在/packages/@vue/cli/bin/vue.js
文件里,可以看到,注册了create
, add
, ui
等命令。Vue CLI 对于代码模块的管理非常细,每个模块基本上都是单一功能模块,可以任意地拼装和使用。
Creator
类
选择其中一个命令create
进行分析,会发现核心其实是在Creator
这个类里。
Preset
预设
Creator
类的实例方法create
接受两个参数:分别是终端命令行传入的参数 cliOptions
和 Vue CLI 的预设preset
。
Q1:什么是 preset
(预设)?
是一个包含创建新项目所需预定义选项和插件的 JSON 对象,让用户无需在命令提示中选择它们。
Q2:什么是 prompt
(终端交互) ?
在 Creator
中的一个方法 getPromptModules
, 按照字面意思,这个方法是获取了一些用于交互的模块,具体来看一下:
获取了一系列的模块,这些模块的代码风格基本统一。其中,injectFeature
就是用户交互的一些配置选项。onPromptComplete
方法会在完成交互后执行。比如下面这个,就是让用户去选择是否使用 babel 的底层实现。
Q3:获取 preset
(预设)
相关逻辑在 create
实例方法之中,如下:
可以看到,代码中分别针对以下 4 种情况作了处理:
● cli 参数配了 --preset
● cli 参数配了 --default
● cli 参数配了 --inlinePreset
● cli 没配相关参数,默认获取 Preset
的行为
Generator
类
这个类负责代码生成。
Q1:初始化插件
在generate
方法中,最先执行的是initPlugins
方法,代码如下:
在这里会给每一个 package.json
里的插件初始化一个 GeneratorAPI
实例,如下:
将实例传入对应插件的 generator
方法并执行,比如下面这个:
Q2: Generator API
类
Vue CLI 使用了一套基于插件的架构。查看一个项目的package.json
,会发现依赖都是以 @vue/cli-plugin-
开头的。插件可以修改 webpack 的内部配置,也可以向 vue-cli-service
注入命令。在项目创建的过程中,绝大部分列出的特性都是通过插件来实现的。
举个例子:
结合上面的源码,可以看到下面的 api 其实就是一个 GeneratorAPI
实例,用到了extendPackage
方法其实就是为了拓展package.json
配置。
让我们默认的package.json
文件添加上下面这些内容:
{
"babel": {
"presets": ["@vue/cli-plugin-babel/preset"]
},
"dependencies": {
"core-js": "^3.6.5"
},
}
总结
核心概念
● Vue CLI 值得学习以及借鉴的地方有很多,比如说整体的交互流程的挂载和插件机制。
● 涉及到的核心概念有 Preset
和 Plugin
,分别表示包括整体的交互流程(Prompt)的预设,以及整体的插件系统。
整个生命周期
● 第 1 步:执行vue create
;
● 第 2 步:实例化Creator
,挂载所有交互配置;
● 第 3 步:调用 creator
实例上的 create
方法;
● 第 4 步:询问用户的自定义配置
● 第 5 步:实例化 Generator
● 第 6 步:初始化各种插件
● 第 7 步:执行插件的 generator
逻辑,写入package.json
文件等
● 最后,将文件写入硬盘,此时项目初始化完毕