vue脚手架启动原理解析

1,025 阅读3分钟

我们一般在创建vue项目时会通过cli脚手架创建,执行vue create hello-world,如果没有安装vue命令可以先全局安装npm install -g @vue/cli,目前我安装的是5.0.4版本的还是比较新的,那我们怎么通过命令去启动整个项目呢。

vue-cli-service serve

当我们下载完脚手架后会执行npm run serve来启动项目,这时什么原因呢,我们来看下它的脚本命令

Snipaste_2022-03-30_14-36-46.png

我们在执行npm run serve时,npm会帮助我们执行vue-cli-service serve,那为什么可以执行vue-cli-service这句命令呢,我们来看看package.json

Snipaste_2022-03-30_15-05-58.png

我们去node_modules@vue/cli-service这个包,在该包的package.json内我们可以找到下面这么一个属性bin

Snipaste_2022-03-30_15-13-28.png

npm在查到当前的bin后会将bin/vue-cli-service.js这个文件做一个软链接放置在node_modules/.bin/目录下,如下所示

Snipaste_2022-03-30_15-20-39.png

所以当我们执行npm run serve实际执行的是node_modules/@vue/cli-service/bin/vue-cli-service.js的这个文件。

vue-cli-service.js

现在来看下vue-cli-service.js这个文件是怎么执行的。

Snipaste_2022-03-30_15-26-26.png

可以看到主要是

  1. 引入Service
  2. new Service一个service对象
  3. service.run

Service.js初始化

我们先来看看Service函数做了什么,Service来自./lib/Service.js

Snipaste_2022-03-30_16-06-29.png

当我们new Service时会先执行构造函数,这里赋了一些初始化的值,这里主要是

  1. package.json内的作为对象赋值给了this.pkg
  2. 将数组对象[{id: '插件路径', apply: '对应插件导出函数'},...]赋值给了this.plugins
  3. 将解析每个命令要使用的默认模式赋值给this.mode

server.run

new Service之后会执行server.run方法

Snipaste_2022-03-30_16-56-07.png

  1. 设置modedevelopment环境,因为我们执行的是vue-cli-service serve
  2. 执行this.init(mode)
  3. 执行fn(args, rawArgv)

server.init

Snipaste_2022-03-30_17-03-36.png

上面我们执行到了this.init,这里主要做了

  1. this.loadEnv(mode),处理环境变量,将process.env.NODE_ENVprocess.env.BABEL_ENV设置为development
  2. this.loadUserOptions()获取用户在vue.config.js下的配置
  3. 执行loadedCallback回调

Snipaste_2022-03-30_17-11-09.png

loadedCallback回调中,主要做了

  1. this.plugins.forEach,调用插件的apply方法,apply就是./commands/serve.js导出的函数,并传入了new PluginAPI(id, this)this.projectOptions这两参数

Snipaste_2022-03-30_17-20-50.png

  1. PluginAPI来自当前目录下的PluginAPI.js文件

Snipaste_2022-03-30_17-44-28.png

可以看到PluginAPIconstructor传入的id就是build-in:commands/serveservice就是之前new的Service实例对象

  1. this.projectOptionsvue-cli默认的配置和vue.config.js合并后的配置

  2. ./commands/serve.js导出的函数中,执行api.registerCommand,就是在执行PluginAPI中的registerCommand方法,name就是serve,所以就是在service.commands上赋值{ fn, opts: opts || {} }

Snipaste_2022-03-30_22-10-36.png

  1. apply执行完后,将配置中的chainWebpackconfigureWebpack分别放入webpackChainFnswebpackRawConfigFns队列中

Snipaste_2022-03-30_22-14-19.png

  1. 最后从service.commamds中导出fn执行

Snipaste_2022-03-30_22-17-11.png

这个fn就是./commands/serve.js导出函数中的最后一个回调参数serve

Snipaste_2022-03-30_22-18-38.png

async function serve()

这个serve回调函数主要做了

  1. 执行api.chainWebpack

Snipaste_2022-03-30_22-21-41.png

api就是PluginApi,即执行service.webpackChainFns.push(fn)

Snipaste_2022-03-30_22-23-39.png

  1. 执行api.resolveWebpackConfig,就是执行service.resolveWebpackConfig方法,可以看到之后执行chainableConfig.toConfig将链式配置转成普通的配置对象,执行this.webpackRawConfigFns.forEach将普通的配置对象进行合并

Snipaste_2022-03-30_22-32-34.png

所以最后就是导出最终可以被webpack解析的配置

Snipaste_2022-03-30_22-27-23.png

  1. 最终就将配置传入webpack,启动webpack-dev-server来启动服务

Snipaste_2022-03-30_22-29-41.png

总结

vue-cli的启动其实还算复杂,但是我们通过断点调试看是能够看清整个启动的流程,在其中我们也发现可以在vue.config.js也可以配置chainWebpack这种链式配置也可以配置configureWebpack这种普通配置。