cli@3.0 插件系统简析(2)

resolvePlugins(inlinePlugins, useBuiltIn) { const idToPlugin = id => ({ id: id.replace(/^.\//, 'built-in:'), apply: require(id) // 加载对应的插件 }) let plugins // @vue/cli-service内部提供的插件 const builtInPlugins = [ './commands/serve', './commands/build', './commands/inspect', './commands/help', // config plugins are order sensitive './config/base', './config/css', './config/dev', './config/prod', './config/app' ].map(idToPlugin) if (inlinePlugins) { plugins = useBuiltIn !== false ? builtInPlugins.concat(inlinePlugins) : inlinePlugins } else { // 加载项目当中使用的插件 const projectPlugins = Object.keys(this.pkg.devDependencies || {}) .concat(Object.keys(this.pkg.dependencies || {})) .filter(isPlugin) .map(idToPlugin) plugins = builtInPlugins.concat(projectPlugins) } // Local plugins if (this.pkg.vuePlugins && this.pkg.vuePlugins.service) { const files = this.pkg.vuePlugins.service if (!Array.isArray(files)) { throw new Error(`Invalid type for option 'vuePlugins.service', expected 'array' but got ${typeof files}.`) } plugins = plugins.concat(files.map(file => ({ id: `local:${file}`, apply: loadModule(file, this.pkgContext) }))) } return plugins }

在这个 resolvePlugins 方法当中,主要完成了对于 @vue/cli-service 内部提供的插件以及项目应用(package.json)当中需要使用的插件的加载,并将对应的插件进行缓存。在其提供的内部插件当中又分为两类:

'./commands/serve'
'./commands/build'
'./commands/inspect'
'./commands/help'

这一类插件在内部动态注册新的 CLI 命令,开发者即可通过 npm script 的形式去启动对应的 CLI 命令服务。

'./config/base'
'./config/css'
'./config/dev'
'./config/prod'
'./config/app'

这一类插件主要是完成 webpack 本地编译构建时的各种相关的配置。@vue/cli-service 将 webpack 的开发构建功能收敛到内部来完成。

插件加载完成,开始调用 service.run 方法,在这个方法内部开始执行所有被加载的插件:

this.plugins.forEach(({ id, apply }) => { apply(new PluginAPI(id, this), this.projectOptions) })

在每个插件执行的过程中,接收到的第一个参数都是 PluginAPI 的实例,PluginAPI 也是整个 @vue/cli-service 服务当中一个核心的基类:

class PluginAPI { constructor (id, service) { this.id = id // 对应这个插件名 this.service = service // 对应 Service 类的实例(单例) } ... registerCommand (name, opts, fn) { // 注册自定义 cli 命令 if (typeof opts === 'function') { fn = opts opts = null } this.service.commands[name] = { fn, opts: opts || {}} } chainWebpack (fn) { // 缓存变更的 webpack 配置 this.service.webpackChainFns.push(fn) } configureWebpack (fn) { // 缓存变更的 webpack 配置 this.service.webpackRawConfigFns.push(fn) } ... }

每个由 PluginAPI 实例化的 api 实例都提供了:

注册 cli 命令服务( api.registerCommand )

通过 api 形式去更新的 webpack 配置( api.chainWebpack )

通过 raw 配置形式去更新的 webpack 配置( api.configureWebpack ),与 api.chainWebpack 提供的链式 api 操作 webpack 配置的方式不同, api.configureWebpack 可接受raw式的配置形式,并通过 webpack-merge 对 webpack 配置进行合并。

resolve wepack 配置( api.resolveWebpackConfig ),调用之前通过 chainWebpack 和 configureWebpack 上完成的对于 webpack 配置的改造,并生成最终的 webpack 配置

...

首先我们来看下 @vue/cli-service 提供的关于动态注册 CLI 服务的插件,拿 serve 服务( ./commands/serve )来说:

// commands/serve module.exports = (api, options) => { api.registerCommand( 'serve', { description: 'start development server', usage: 'vue-cli-service serve [options] [entry]', options: { '--open': `open browser on server start`, '--copy': `copy url to clipboard on server start`, '--mode': `specify env mode (default: development)`, '--host': `specify host (default: ${defaults.host})`, '--port': `specify port (default: ${defaults.port})`, '--https': `use https (default: ${defaults.https})`, '--public': `specify the public network URL for the HMR client` } }, async function serve(args) { // do something } ) }

./commands/serve 对外暴露一个函数,接收到的第一个参数 PluginAPI 的实例 api,并通过 api 提供的 registerCommand 方法来完成 CLI 命令(即 serve 服务)的注册。

再来看下 @vue/cli-service 内部提供的关于 webpack 配置的插件( ./config/base ):

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/a0f22b73d631940d731e2859e4e9925f.html