Bootstrap

分析 Vue CLI 5源码(基于最新版源码进行分析!)

一、前言

最近抽时间研究了一下 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 为什么要用脚手架?

前端项目初始化的发展历程

  1. 手动创建。通过手动创建.html, .css, .js 文件,在 .html 里引入.css.js文件。
  2. 模板托管。将项目模板代码托管到 Git/SVN 仓库上,使用的时候先手动将代码拉到本地,然后修改文件夹名以及项目中的配置。
  3. 自动化脚本。通过脚本,自动化拉取项目模板、配置项目信息等。
  4. 完整的脚手架。通过命令行 vue-cli/ create-react-app 初始化项目。
  5. 自定义扩展。在 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 值得学习以及借鉴的地方有很多,比如说整体的交互流程的挂载和插件机制。
● 涉及到的核心概念有 PresetPlugin,分别表示包括整体的交互流程(Prompt)的预设,以及整体的插件系统。

整个生命周期

● 第 1 步:执行vue create
● 第 2 步:实例化Creator,挂载所有交互配置;
● 第 3 步:调用 creator 实例上的 create方法;
● 第 4 步:询问用户的自定义配置
● 第 5 步:实例化 Generator
● 第 6 步:初始化各种插件
● 第 7 步:执行插件的 generator 逻辑,写入package.json文件等
● 最后,将文件写入硬盘,此时项目初始化完毕

;