vue-cli3 的快速插件开发

前言

不久前组内有大佬发布了一个 vue-cli3 的 dll 包,做为一个在 vue 项目内摸爬滚打的萌新,是时候该学习点儿新的技术了,因而在闲暇之余,我拷贝了一份代码,同时研究该如何从“零”开始编写一个 dll 包(该部分以 webpack 的 dllPlugin 做为例子)。html

了解文档

通篇读完官网的文档,由于知识点比较多,而且没有详细的例子(指“傻瓜式教学式”),所以开发这第三方包的学习成本仍是有一些的,特别是 webpack-chain 和 node 的部分知识,这里记录总结了一些关键的点。前端

插件命名

最开始命名文件夹时我并无使用 vue-cli-plugin- 做为文件名前缀,结果可想而知,vue invoke 一直提示找不到该包的信息。因而我去看了看源码…… 在 @vue/cli/lib/invoke.js 内,其有一个关键的获取包 Id 的方法 resolvePluginId,该方法在 @vue/cli-shared-utils/lib/pluginResolution.js,源码以下:vue

exports.resolvePluginId = id => {
  // already full id
  // e.g. vue-cli-plugin-foo, @vue/cli-plugin-foo, @bar/vue-cli-plugin-foo
  if (pluginRE.test(id)) {
    return id
  }
  // scoped short
  // e.g. @vue/foo, @bar/foo
  if (id.charAt(0) === '@') {
    const scopeMatch = id.match(scopeRE)
    if (scopeMatch) {
      const scope = scopeMatch[0]
      const shortId = id.replace(scopeRE, '')
      return `${scope}${scope === '@vue/' ? `` : `vue-`}cli-plugin-${shortId}`
    }
  }
  // default short
  // e.g. foo
  return `vue-cli-plugin-${id}`
}
复制代码

很明显,使用 vue invoke 时其只会寻找含有 vue-cli-plugin- 做为前缀的包,官网内在文档的最后部分有作对应的说明(这个是后来才看到的),原文以下:node

为了让一个 CLI 插件可以被其它开发者使用,你必须遵循 vue-cli-plugin-<name> 的命名约定将其发布到 npm 上。webpack

所以 package.jsonname 字段符合规则便可。git

creator 和 service

官网开篇就介绍了两个主要的部分:@vue/cli@vue/cli-service,首先是 @vue/cli 部分,这里介绍了插件的目录结构,所以咱们能够根据此来搭一个插件框架:github

vue-cli-plugin-xxx
  ├── README.md
  ├── generator
  |  └── index.js
  ├── index.js
  ├── package.json
  ├── prompts
  |  └── index.js
  ├── service
  |  ├── config-file.js
  |  └── regist-command.js
  └── yarn.lock
复制代码

接下来就详细分析一下各部分的做用。web

generator

文档分析

文档中有提到,插件内的 generator 将会在两种场景下被调用:vue-cli

  • 在一个项目的初始化建立过程当中,若是 CLI 插件做为项目建立 preset 的一部分被安装
  • 插件在项目建立好以后经过 vue invoke 独立调用时被安装

因为开发的第三方插件使用场景多数在于更改已安装的项目配置,preset 使用场景不是不少(建立项目时通常仍是手动配置,大多数状况不会选择去生成一个 ~/.vuerc),所以这边仅处理使用手动调用 generator 的状况。npm

触发 generator 的方法有两种:

  • vue invoke
  • vue add

下面就简单介绍一下这两个命令的区别。

vue invoke 指令

此指令的适用状况为已经经过 yarn 或者 npm 将包安装至项目内,此时仅须要调用 vue invoke 便可。

注意:这里的 packageName 为不包含 vue-cli-plugin- 部分的剩余包名,好比:发布的包名为 vue-cli-plugin-xxx,那么此时使用命令即 vue invoke xxx

vue add

此指令的使用状况为项目内尚未安装对应的包,使用方式同 vue invoke

注:若是包的源不对的话,请本身在后面加上包所在的 npm 源地址( --registry )

内容编写

分析了这么多,重点仍是 generator 内咱们应该写点什么,它影响的是什么。好了,让咱们来继续看文档(● ˃̶͈̀ロ˂̶͈́)੭ꠥ⁾⁾

generator 有三个参数,这里就不细赘了,由于这里不关注 preset 的配置,因此对咱们来讲,有用的部分就只有第一个参数 api。首先咱们须要改动的部分即是项目内的 package.json 了,使用方法 extendPackage 便可,例子以下:

// 修改 `package.json` 里的字段
// vue 部分的内容能够不要
module.exports = (api, options, rootOptions) => {
  // 修改 `package.json` 里的字段
  api.extendPackage({
    scripts: {
      test: 'vue-cli-service test'
    },
    vue: {
      pluginOptions: {
        test: {
          // 须要预打包的部分
          vendors: [],
          // 输出文件名
          outputName: 'vendor.dll.js',
          // 输出地址
          outputPath: './public/vendor',
          // 是否调用 cleanWebpackPlugin
          cleanCache: true
        }
      }
    }
  });
}
复制代码

关于此处添加的 vue 字段在 invoke 后会自动补充至 vue.config.js 或者 package.json 内。

若是你配置了 promots 而且须要该部分的内容,那么可使用第二个 options 参数去获取配置的内容。(配置 .vuerc 的方法没有尝试,由于解构 + 默认值 + prompts 已经足够了)

若是须要配置模板方面的参考官方源码,感受配合 prompts 写个模板插件也不错。

友情提示:render() 函数内为你的 template 模板基于当前文件夹所在的路径。

prompts

该部分其实在这个项目内并无涉及,但仍是要提一下。官方文档对于此部分在内建插件有详细的说明(官方插件),第三方插件提到过一点:

这个文件应该导出一个用于 Inquirer.js 的问题的数组。这些被解析的答案对象会做为选项被传递给插件的 generator。

所以,若是须要的状况下,咱们得经过数组的形式编写问答。例如:

module.exports = [
  {
    name: 'entry',
    message: "What's the output file's name?",
    type: 'input',
    default: 'vendor'
  }
]
复制代码

此部分的配置结果会在 generator 部分的第二个参数捕捉到。

service

这部分就是配置的重点了,仍是根据官网来吧,官网有提到 3 个命令: chainWebpackconfigureWebpackregisterCommand。直接更改原有项目的配置并非很好(除非你颇有信心),所以咱们可使用 configureWebpack 来合并变动。

这部分总的来讲作三件事:

  1. 更改用户的 webpack 配置文件(也就是 vue.config.js)
  2. 向 cli-service 内注册指令
  3. 为注册的指令指定模式

这里从简单的部分开始一一说明吧~(顺序:三、一、2)

指定模式

嗯,这个最简单了,毕竟官网文档内有,理由也就不赘述了,代码以下:

module.exports.defaultModes = {
  <your direct>: <target mode> } 复制代码

其中 your direct 部分即先前的 generator 部分注册的 script 脚本内,在 vue-cli-service 后面的那个指,好比,先前写的是 test,那么这里注册的指令也是 direct,mode 就根据实际状况处理便可,通常使用 production 生产模式就没啥问题。

更改用户的配置文件

这里就须要有 webpack-chain 的知识了。先看看 pluginAPI 内有些啥(传送门),可能会用到的一些方法包括:

  • getCwd:获取当前的工做目录
  • resolve:至关于 path.resolve
  • registerCommand:注册指令(有三个参数!)
  • chainWebpack:链式调用 webpack
  • configureWebpack:用于合并 webpack
  • resolveChainableWebpackConfig:用于解析 webpack

此处咱们使用 configureWebpack 来更改配置,同时使用 registerCommand 来注册咱们的命令。这里恰好对应的就是咱们的两个文件 config-fileregist-command.js 了,对应开发便可。

configureWebpack

参考源码的写法,咱们能够经过 options 参数获取 invoke 生成的 vue.config.js 里面的 pluginOptions 字段内的对应配置内容,配合 api.configureWebpack 来注入咱们的 webpack 配置。粗略的写法为:

api.configureWebpack(config => {
  // 增一个 plugins
  config.plugins.push(
    /* plugin 配置 */
  )
});
复制代码
registerCommand

查看源码的写法,发现参数有三个,分别是:

  • api: pluginAPI 实例
  • options: 用来添加配置说明
  • fn: 回调函数,用于触发执行的内容(好比运行一个 webpack 配置)

官方的写法以下,咱们能够据此模仿

api.registerCommand(
  "test",
  {
    description: "此为指令的说明", // 指令意义
    usage: "vue-cli-service test", // 命令怎么用
    options: {
      /* 参数说明 */
    }
  },
  async args => {
    /* 能够写个 chain-webpack 而后调用,或者干点其它的 */
  }
);
复制代码

部分问题总结

1. demo 内的 dll 打包位置问题

使用默认的打包位置: public/vendor 会产生一个问题,就是 build 文件内会包含此 vendor 文件夹,咱们注入的一些配置不生效。

解决方法:将默认的 public/vendor 打包位置改改,只要不是生成在 public 文件夹内的都没问题,可看 vue-cli 官网的 public 部分的解释。

2. webpack 函数

目前第二个回调参数的使用场景不是很明确(其实不用回调也能够,外层关闭对应的 log 便可)

3. configureWebpack 只能用 push?

我尝试过本身新建立一个 webpack-chain 而后返回(源码上好像会对返回值进行判断,若是有那么会调用 merge 方法),按理来讲返回一个 config.toConfig() 应该 没问题,可是行不通,目前仍然用的是 push 方法

demo

传送门

参考文档

--by kazehaiya

相关文章
相关标签/搜索