引子
最近想搞了一个自用的CLI,于是读一下vue-cli3的源码,取一下经。
vue-cli3采用的是一套基于插件的架构。模板、配置文件的添加和修改都交由插件来完成的,vue-cli只负责将最后的结果渲染出来。
它的插件架构主要涉及Generator和GeneratorAPI这两个核心类,Creator类负责的是整个CLI的运行流程。
GeneratorAPI
这个类的工作就是向插件提供访问Generator实例的接口,这些接口可以在Vue CLI官网上可以进一步了解,这里只是简单提一下,做个铺垫。
- api.render() 主要用来创建新的模板
- api.extendPackage() 添加新的依赖或者npm脚本,就是负责修改package.json文件
- api.hasPlugin() 判断该项目是否使用了某个插件
- api.postProcessFiles 保存插件想要在渲染完模板之后所要执行的事情。
Generator
首先来看下它的样子:
class Generator {
constructor(context, { pkg, plugins, files }) {
// 项目的路径
this.context = context
// CLI内置的插件:负责创建vue项目
this.plugins = sortPlugins(plugins)
// 最后要生成的pacakge.json文件
this.pkg = Object.assign({}, pkg)
// 保存要渲染的文件及其目录
this.files = Object.keys(files).length
? watchFiles(files, this.filesModifyRecord = new Set())
: files
// 保存插件的文件渲染任务
this.fileMiddlewares = []
// 保存插件在文件渲染完成后还需要执行的任务
this.postProcessFilesCbs = []
// load all the other plugins
this.allPlugins = this.resolveAllPlugins()
// 整个Vue Service的配置
this.rootOptioins = {}
const cliService = plugins.find(p => p.id === '@vue/cli-service')
const rootOptions = cliService
? cliService.options
: inferRootOptions(pkg)
this.rootOptions = rootOptions
}
async generate() {
// 内部执行流程
await this.initPlugins()
this.extractConfigFiles()
await this.resolveFiles()
this.files['package.json'] = JSON.stringfy(this.pkg, null, 2)
await writeFileTree(this.context, this.files)
}
}
实际上里面还有一些属性,不过基本都是为vue定制了,所以没标出来。Creator将Generator类实例化之后,就将新建项目的路径、所需要的插件都都传了进来,然后调用这个实例的generate方法。
这个initPlugins的工作,就是遍历this.plugins里面的所有插件,并给每个插件实例化一个GeneratorAPI(插件id, Generator实例,插件配置,rootOptions),然后调用插件的generator,并将GeneratorAPI实例传进去。插件可以根据这个api实例实现的一些接口访问到Generator里面的属性。比如说:
@vue/cli-service就会使用api.render()将渲染vue基础模板的函数放到this.fileMiddlewares里面,以及使用api.extendPackage()添加vue的依赖到this.pkg里,如下图所示
@vue/cli-plugin-typescript就会通过调用api.postProcessFiles()在this.postProcessFilesCbs里放入在vue项目模板解析完后将.js文件改成.ts文件的处理函数。
将所有插件的generator都执行完后,接着就来执行extractConfigFiles方法了。这个方法的工作就是将this.pkg里面放的配置,比如说.eslintrc.js、babel.config.js、vue.config.js这些文件的配置,拿出来放到this.files里面,因为vue-cli的思路是将所有东西都放到this.pkg里面,然后再逐一处理,抽离出来。
接下来来到resolveFiles,这个方法内部逻辑就是遍历执行this.fileMiddlewares和this.postProcessFilesCbs里面的每个函数。像this.fileMiddlewares放的都是模板的渲染函数,执行完后,就将模板内容放到this.files里,并以这个模板文件相对路径作为键,比如说src/App.vue: '模板内容'。this.postProcessFilesCbs里的函数也是负责处理this.files里的数据,比如说cli-plugins-typescript就会将this.files里面一些以.js结尾的属性都改成.ts。
实际上这个方法里面还有一些是处理vue的,想了解的可以去看一下,大致上就是添加import以及注入一些变量等工作。
最后就是遍历this.files,通过fs模块将vue模板以及配置文件写入磁盘。
以上便是vue cli插件架构的大致流程,第一次写文章,紧张!
期间这个vue cli解析系列帮助蛮大的!在此感谢!