简单的vue cli3的插件系统解析

359 阅读3分钟

引子

最近想搞了一个自用的CLI,于是读一下vue-cli3的源码,取一下经。

vue-cli3采用的是一套基于插件的架构。模板、配置文件的添加和修改都交由插件来完成的,vue-cli只负责将最后的结果渲染出来。

它的插件架构主要涉及GeneratorGeneratorAPI这两个核心类,Creator类负责的是整个CLI的运行流程。

GeneratorAPI

这个类的工作就是向插件提供访问Generator实例的接口,这些接口可以在Vue CLI官网上可以进一步了解,这里只是简单提一下,做个铺垫。

  1. api.render() 主要用来创建新的模板
  2. api.extendPackage() 添加新的依赖或者npm脚本,就是负责修改package.json文件
  3. api.hasPlugin() 判断该项目是否使用了某个插件
  4. 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定制了,所以没标出来。CreatorGenerator类实例化之后,就将新建项目的路径、所需要的插件都都传了进来,然后调用这个实例的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里,如下图所示

image.png

  • @vue/cli-plugin-typescript就会通过调用api.postProcessFiles()this.postProcessFilesCbs里放入在vue项目模板解析完后将.js文件改成.ts文件的处理函数。

将所有插件的generator都执行完后,接着就来执行extractConfigFiles方法了。这个方法的工作就是将this.pkg里面放的配置,比如说.eslintrc.jsbabel.config.jsvue.config.js这些文件的配置,拿出来放到this.files里面,因为vue-cli的思路是将所有东西都放到this.pkg里面,然后再逐一处理,抽离出来。

接下来来到resolveFiles,这个方法内部逻辑就是遍历执行this.fileMiddlewaresthis.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解析系列帮助蛮大的!在此感谢!