vuecli源码解析(3)

vue-cli-service作为第一个必然会被调用的plugin,其功能主要是生成项目模板文件,以及通过serve、build等命令进行开发环境server的启动和生产环境的构建打包等。下面分析其内部逻辑。
检查 Node 版本
vue-cli-service位于@vue/cli-service目录下,通过package.json找到其入口程序为bin/vue-cli-service.js,首先依旧是检查 Node 版本。
初始化Service实例
传入执行当前命令的目录,也就是创建项目的文件夹,来初始化Service实例。

加载项目运行程序和webpack配置项

resolvePlugins会从上层的commands以及config目录加载进来所有index.js文件。commands目录下包括开发环境执行程序serve.js以及生产环境打包build.js等,config目录包含webpack的配置程序,都是项目运行所需的程序,因为通过@vue/cli创建的项目不会暴露webpack等配置项,所以需要这些程序来执行。
这些程序被看做plugin,包含一个id作为标识符,和一个apply方法,需要执行的时候调用apply方法即可。

然后还会对plugins进行排序。
serve.js
serve.js是开发环境下启动 webpack dev server 的程序,其返回的是一个函数,函数内部通过调用api.registerCommand传递了三个参数,那么serve.js通过plugin的方式注册,肯定会有执行的时候,见下文继续探索。

并且导出的模块还有一个defaultModes对象,包含命令名称和环境变量参数的映射关系,这样Service实例内部可以通过defaultModes获取当前程序执行的环境。


build.js
build.js是生产环境的打包程序,则通过调用api.registerCommand传递build字符串等参数,以及暴露了production的环境变量。


css.js
在config目录下的css.js为例,其通过api.chainWebpack来配置webpack

注册命令行参数
初始化完Service实例,加载完项目所需要的所有执行程序,然后会执行run方法,run内部首先会走init方法。


加载env
在init方法内部会加载指定环境的.env文件内部设置环境变量参数,这里使用了dotenv和dotenv-expand这两个库来加载.env文件的环境变量到process.env中。


获取项目配置项
在加载了环境变量参数以后,会调用loadUserOptions方法,loadUserOptions内部会调用loadFileConfig获取项目内部的vue.config.js等形式的配置文件。

然后用户项目配置项会传入loadedCallback函数内部,使用lodash.defaultsDeep和vue-cli-service内部的默认配置合并。


注入PluginAPI实例
loadedCallback内部会调用每个plugin的apply方法,传入PluginAPI的实例和当前项目的配置项作为参数。

其中PluginAPI在初始化的时候传递了两个参数:
id:plugin程序的名称this:指向当前的Service实例
在构造函数内部初始化以后,Service实例会挂载在PluginAPI实例的service属性上。

上文介绍到apply也就是serve.js等构建模块导出的函数,其接收两个参数:
api:PluginAPI的实例options:项目的配置项
PluginAPI内部定义了一系列方法可以通过api来调用,其中api.registerCommand是往Service实例的commands上注册vue-cli-service的 CLI 命令参数名称serve,不过这里只是注册,并没有开始执行,所以到这里程序的目的只是将构建项目需要的程序加载进来,并且和命令行参数对应起来而已。


后面的话,如果在配置文件中包含chainWebpack和webpackConfig配置项也会挂载到Service实例的webpackChainFns和webpackRawConfigFns属性上。
组合webpack配置项
上文说到config目录下的程序会通过api.chainWebpack注册一些 webpack 的配置项,这些配置项会保存在Service实例的webpackChainFns和webpackRawConfigFns内部。

执行程序
命令行参数现在和执行程序已经通过init方法关联起来了,接下来便是找到在 CLI 输入的命令参数对应的执行程序,然后执行。
接着看run方法内部的逻辑。这里通过this.commands找到了和命令行参数name对应的执行程序command,然后便调用command.fn这个方法,也就是serve.js内部传递的回调函数,也就是开发环境启动 webpack dev server 等操作。

