不久前组内有大佬发布了一个 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.json
的 name
字段符合规则便可。git
官网开篇就介绍了两个主要的部分:@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 将会在两种场景下被调用:vue-cli
vue invoke
独立调用时被安装因为开发的第三方插件使用场景多数在于更改已安装的项目配置,preset
使用场景不是不少(建立项目时通常仍是手动配置,大多数状况不会选择去生成一个 ~/.vuerc),所以这边仅处理使用手动调用 generator 的状况。npm
触发 generator
的方法有两种:
vue invoke
vue add
下面就简单介绍一下这两个命令的区别。
此指令的适用状况为已经经过 yarn 或者 npm 将包安装至项目内,此时仅须要调用 vue invoke 便可。
注意:这里的 packageName 为不包含
vue-cli-plugin-
部分的剩余包名,好比:发布的包名为vue-cli-plugin-xxx
,那么此时使用命令即vue invoke xxx
此指令的使用状况为项目内尚未安装对应的包,使用方式同 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 模板基于当前文件夹所在的路径。
该部分其实在这个项目内并无涉及,但仍是要提一下。官方文档对于此部分在内建插件有详细的说明(官方插件),第三方插件提到过一点:
这个文件应该导出一个用于 Inquirer.js 的问题的数组。这些被解析的答案对象会做为选项被传递给插件的 generator。
所以,若是须要的状况下,咱们得经过数组的形式编写问答。例如:
module.exports = [
{
name: 'entry',
message: "What's the output file's name?",
type: 'input',
default: 'vendor'
}
]
复制代码
此部分的配置结果会在 generator 部分的第二个参数捕捉到。
这部分就是配置的重点了,仍是根据官网来吧,官网有提到 3 个命令: chainWebpack
、configureWebpack
、registerCommand
。直接更改原有项目的配置并非很好(除非你颇有信心),所以咱们可使用 configureWebpack
来合并变动。
这部分总的来讲作三件事:
这里从简单的部分开始一一说明吧~(顺序:三、一、2)
嗯,这个最简单了,毕竟官网文档内有,理由也就不赘述了,代码以下:
module.exports.defaultModes = {
<your direct>: <target mode> } 复制代码
其中 your direct
部分即先前的 generator 部分注册的 script 脚本内,在 vue-cli-service
后面的那个指,好比,先前写的是 test
,那么这里注册的指令也是 direct
,mode 就根据实际状况处理便可,通常使用 production
生产模式就没啥问题。
这里就须要有 webpack-chain 的知识了。先看看 pluginAPI 内有些啥(传送门),可能会用到的一些方法包括:
此处咱们使用 configureWebpack
来更改配置,同时使用 registerCommand
来注册咱们的命令。这里恰好对应的就是咱们的两个文件 config-file
和 regist-command.js
了,对应开发便可。
参考源码的写法,咱们能够经过 options 参数获取 invoke 生成的 vue.config.js
里面的 pluginOptions
字段内的对应配置内容,配合 api.configureWebpack
来注入咱们的 webpack 配置。粗略的写法为:
api.configureWebpack(config => {
// 增一个 plugins
config.plugins.push(
/* plugin 配置 */
)
});
复制代码
查看源码的写法,发现参数有三个,分别是:
官方的写法以下,咱们能够据此模仿
api.registerCommand(
"test",
{
description: "此为指令的说明", // 指令意义
usage: "vue-cli-service test", // 命令怎么用
options: {
/* 参数说明 */
}
},
async args => {
/* 能够写个 chain-webpack 而后调用,或者干点其它的 */
}
);
复制代码
使用默认的打包位置: public/vendor
会产生一个问题,就是 build 文件内会包含此 vendor 文件夹,咱们注入的一些配置不生效。
解决方法:将默认的 public/vendor
打包位置改改,只要不是生成在 public 文件夹内的都没问题,可看 vue-cli 官网的 public 部分的解释。
目前第二个回调参数的使用场景不是很明确(其实不用回调也能够,外层关闭对应的 log 便可)
我尝试过本身新建立一个 webpack-chain 而后返回(源码上好像会对返回值进行判断,若是有那么会调用 merge 方法),按理来讲返回一个 config.toConfig() 应该 没问题,可是行不通,目前仍然用的是 push 方法
--by kazehaiya