1

vuecli源码解析(1)

 2 years ago
source link: https://icodex.me/2022/01/23/vuecli%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%901/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

vuecli源码解析(1)

2022年1月23日 · One min read

image-20220122161749258

vue-cli是 vue 官方出品的脚手架项目,用来快速搭建vue项目,一键生成项目基础代码。

一直对其内部的运行原理比较好奇,并且开发脚手架的能力也是一名前端开发人员需要掌握的技能,所以这里以v4.5.15版本为例,记录一下源码研究的过程和学习点。

入口

vue-cli采用的仍然是通过lerna管理的 monorepo,不知道未来会不会迁移到 pnpm,从package.json可以看到核心仓库就是以下三个,其中@vue就是一些核心代码,包括脚手架@vue/clivue-cli-service以及一些插件。

image-20220122103920356

vue-cli
├─ docs // 文档
├─ packages
│ ├─ @vue
│ │ ├─ babel-preset-app
│ │ ├─ cli // @vue/cli
│ │ ├─ cli-init
│ │ ├─ cli-overlay
│ │ ├─ cli-plugin-babel
│ │ ├─ cli-plugin-e2e-cypress
│ │ ├─ cli-plugin-e2e-nightwatch
│ │ ├─ cli-plugin-e2e-webdriverio
│ │ ├─ cli-plugin-eslint
│ │ ├─ cli-plugin-pwa
│ │ ├─ cli-plugin-router
│ │ ├─ cli-plugin-typescript
│ │ ├─ cli-plugin-unit-jest
│ │ ├─ cli-plugin-unit-mocha
│ │ ├─ cli-plugin-vuex
│ │ ├─ cli-service // vue-cli-service
│ │ ├─ cli-shared-utils
│ │ ├─ cli-test-utils // 一些第三库的源代码,或者一些工具函数等
│ │ ├─ cli-ui
│ │ ├─ cli-ui-addon-webpack
│ │ └─ cli-ui-addon-widgets
├─ scripts // 一些管理monorepo的程序

@vue/cli

先从脚手架启动引导程序@vue/cli开始分析,从package.json注册的bin属性找到入口程序为bin/vue.js.

{
"bin": {
"vue": "bin/vue.js"
},
}

检查Nodejs版本

首先会通过semver这个库检查使用者本地 Nodejs 的版本和在@vue/clipackage.json下要求的版本是否符合。

image-20220122105655018

这里简单了解下 Nodejs 的版本管理,Nodejs 版本有以下几种:

  • CURRENT:当前状态,也就是当前最新的 Nodejs 版本,ACTIVE版本的 Nodejs 会维护持续 6 个月时间,6 个月之后奇数版本会不再维护,而偶数版本会变成ACTIVE状态的LTS版本
  • ACTIVE:活跃状态,是正在积极维护和升级的版本,包括一些 BUG 修复,功能改进等
  • MAINTENANCE:维护状态,只修复 BUG,维护时间不定
  • EOL:End of Life,也就是终止维护的版本
  • LTS:long-term support,也就是长期维护版本,这意味着重大的 Bug 将在后续的 30 个月内持续得到不断地修复。

如下图所示,Nodejs 12 已进入维护状态,并且在 2022 年 4 月份就会终止维护,到时候Nodejs 18 也会发布,Nodejs 17 也会终止维护。 反正一个原则是始终用 LTS 版本就行了。可以使用nvm便捷的管理 Nodejs 的版本。

注册命令

然后使用commanderjs注册命令create,并且包含必填的app-name参数,可以看到create注册以后,会去加载上层lib下的create.js程序,这里还传递了项目名称和额外的 CLI 参数。

image-20220122125035080

校验项目名称

create.js内部首先会通过validate-npm-package-name这个库去校验项目名称,并且会额外处理使用.作为当前目录的情况,考虑的非常周到。

image-20220122150200380

如果通过 CLI 指定--merge,则会清空目标文件夹,否则会使用inquirer在 CLI 发起选项选择是否合并目录文件或者选择清空。这里使用了fs-extra来操作文件系统。

初始化Creator实例

在确认了目标文件夹以后,会初始化Creator的实例,并传递 CLI 参数来调用实例的create方法。

image-20220122131148494

这里传递了三个初始化参数:

  • name:项目名称
  • targetDir:创建项目的文件夹
  • getPromptModules():加载位于上层promptModules文件夹下的一些函数

image-20220122134405339

位于promptModules下的都是一些用户在创建项目时手动选择的功能项,例如vue的版本,是否使用 TS,CSS预处理器等。

../promptModules/babel.js为例,可以看到其内部是一个函数,接收一个cli对象,并且调用了cli对象暴露的injectFeatureonPromptComplete这两个方法。

image-20220122143703009

获取prompt选项

image-20220122132912054

Creator的构造函数内部会初始化一些实例属性:

  • name:项目名称

  • context:当前创建的目录

  • presetPromptpreset的选项;通过resolveIntroPrompts获取preset信息,其中包括加载用户创建的保存在本地.vuerc下的preset,以及@vue/cli内置默认的preset信息,然后组合成inquirer选项参数保存在这个属性下

image-20220122155504150

默认的preset只有两个:选择vue2vue3的版本,并且都会包含@vue/cli-plugin-babel@vue/cli-plugin-eslint两个plugin

image-20220122143427655

  • featurePrompt:如果用户选择不使用任何preset而是手动选择一些功能来创建项目,例如是否使用 TS,选择 CSS 预处理器等,那么就会从featurePrompt加载一些选项

image-20220122135142250

  • outroPrompts:主要是通过inquirer指定的一些选项,例如保存配置文件的方式、选择使用的依赖管理工具npm,yarn,pnpm等

之后会通过传入当前实例对象this来初始化一个PromptModuleAPI的实例。

image-20220122135843585

PromptModuleAPI内部会往自身的实例上挂载一个creator对象,也就是Creator的实例,这样在PromptModuleAPI内部的实例就可以通过creator对象访问Creator的实例内部的属性和方法。

image-20220122135238009

这时候初始化Creator实例时传入的promptModules内部的每个函数就会被调用,并传入PromptModuleAPI的实例作为参数,每个函数内部通过调用PromptModuleAPI内部的方法再向Creator实例内部的属性注入自身的prompt选项,不得不说逻辑有点绕,但是提高了程序的拓展性,以后@vue/cli内部想拓展一些功能,只需要在promptModules文件夹下编写程序即可。

  • injectFeature:往实例的featurePrompt.choices推入一些feature
  • injectPrompt:往实例的injectedPrompts推入一些prompt
  • injectOptionForPrompt:往injectedPrompts.choices推入一些选项
  • onPromptComplete:往实例的promptCompleteCbs推入回调函数,在prompt执行完以后执行

组合prompt并获取preset

初始化Creator实例以后会调用create方法,接收 CLI 命令行指定的所有参数,在使用vue create xx命令没有指定任何其他参数的情况下会进入promptAndResolvePreset方法。promptAndResolvePreset内部主要做了两件事:

  • 合并Creator实例的presetPromptfeaturePromptinjectedPrompts以及outroPrompts这些选项,并且默认的选项presetPrompt作为第一个,后续的prompt会通过inquirerwhen函数来判断其是否需要执行,如果用户选择不使用preset,那么才会执行后续手动选择feature的部分

image-20220122154431693

  • 当用户选择了preset以后,就会通过resolvePreset再次获取preset的信息

image-20220122154736822

注入vue-cli-service

vue-cli-service在这里也是作为一个plugin,上文获取preset信息以后,会在其默认plugins的基础上再注册vue-cli-service

image-20220122160008434

生成package.json

获取所有plugin以后,会创建package.json对象,写入plugin的版本,并生成文件

image-20220122161035813

初始化git

如果通过 CLI 指定初始化 git,这里还会调用git init命令,初始化 git 本地存储服务。

image-20220123221844974

安装依赖

@vue/cli内部定义了依赖管理基类PackageManager,内部会判断客户端使用的依赖管理工具,使用的源地址等信息,代码很多,就不一一展开了。这里安装的依赖从前面来看主要有三个:

  • @vue/cli-plugin-babel
  • @vue/cli-plugin-eslint
  • @vue/cli-service

image-20220123222843578


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK