通过vue-cli插件方式定制自己的模板,cli插件包括以下功能: 1.修改webpack配置 2.与用户进行交互,用户选择所需的一些功能模块 3.扩展package.json 4.创建、修改生成的项目文件 5.添加新的 vue-cli-service 命令
插件文件结构
通常的 CLI 插件目录结构看起来像下面这样:
.
├── README.md
├── generator.js 或者generator文件夹 # 修改或者生成项目文件(可选)
├── index.js # service 插件
├── package.json
├── prompts.js # 交互文件(可选)
└── ui.js # Vue UI 集成(可选)
插件命名规范
为了让一个 CLI 插件在 Vue CLI 项目中被正常使用,它必须遵循 vue-cli-plugin-<name> 或者 @scope/vue-cli-plugin-<name> 这样的命名惯例。例如:vue-cli-plugin-template-preset,这样你的插件才能够:
- 被
@vue/cli-service发现; - 被其他开发者通过搜索发现;
- 通过
vue add <name>或者vue invoke <name>安装
插件安装
-
本地安装
// 1.如果没有项目就创建个新的 vue create my-app // 2.安装本地插件 npm install --save-dev file:/full/path/to/your/plugin vue invoke vue-cli-plugin-template-preset -
使用preset自动在线安装插件
vue create --preset vue-cli-plugin-template-preset my-app -
手动在线安装
vue create my-app cd my-app vue add vue-cli-plugin-template-preset
Generator修改文件
在 CLI 插件内部,generator 应该放在
generator.js或者generator/index.js文件中。它将在以下两个场景被调用:
-
项目初始创建期间,CLI 插件被作为项目创建 preset 的一部分被安装时。
-
当插件在项目创建完成和通过
vue add或者vue invoke单独调用被安装时。 -
一个 generator 应该export一个接收三个参数的函数
/** * generator/index.js * api: <https://cli.vuejs.org/dev-guide/generator-api.html> * options: prompt用户交互结果或者 `~/.vuerc`中的option值 * rootOptions: `~/.vuerc`中的该preset整个值,相当于下面例子的 foo 对象值 */ module.exports = (api, options, rootOptions) => { // 使用提供的api或者option进行一些定制 }// ~/.vuerc文件格式 { "presets" : { "foo": { "plugins": { "@vue/cli-plugin-foo": { "option": "bar" } // generator拿到的option值 } } } }
一些常用的generator api
-
api.render('./template')渲染EJS模板 -
api.extendPackage -
api.entryFile -
api.afterInvoke -
api.chainWebpack -
api.registerCommand// generator.js module.exports.hooks = (api) => { api.afterInvoke(() => { const { EOL } = require('os') const fs = require('fs') const contentMain = fs.readFileSync(api.resolve(api.entryFile), { encoding: 'utf-8' }) const lines = contentMain.split(/\r?\n/g)
const renderIndex = lines.findIndex(line => line.match(/render/)) lines[renderIndex] += `${EOL} router,` fs.writeFileSync(api.entryFile, lines.join(EOL), { encoding: 'utf-8' })}) }
模板文件生成
-
新文件:
// 放到template文件夹下,然后用api.render解析 module.exports = api => { api.render('./template') } -
编辑已存在的文件
-
方式一:
--- extend: '@vue/cli-service/generator/template/src/App.vue' // 目标文件 replace: !!js/regexp /<script>[^]*?<\\/script>/ // 正则匹配内容 --- <script> export default { // Replace default script } </script> -
方式二:多处替换
--- extend: '@vue/cli-service/generator/template/src/App.vue' replace: - !!js/regexp /Welcome to Your Vue\\.js App/ - !!js/regexp /<script>[^]*?<\\/script> --- <%# REPLACE %> 替换欢迎信息 <%# END_REPLACE %> <%# REPLACE %> <script> export default { // 替换默认脚本 } </script> <%# END_REPLACE %>
-
3.扩展package.json
module.exports = api => {
api.extendPackage({
dependencies: {
'vue-router-layout': '^0.1.2'
}
})
}
4.主文件注入
module.exports.hooks = (api) => {
api.afterInvoke(() => {
const { EOL } = require('os')
const fs = require('fs')
// 读取文件
const contentMain = fs.readFileSync(api.resolve(api.entryFile), { encoding: 'utf-8' })
// 文件内容分行
const lines = contentMain.split(/\\r?\\n/g)
// 查找到render那一行
const renderIndex = lines.findIndex(line => line.match(/render/))
lines[renderIndex] += `${EOL} router,`
// 写入
fs.writeFileSync(api.entryFile, lines.join(EOL), { encoding: 'utf-8' })
})
}
5.修改webpack配置
对话
所有的对话逻辑都存储在 prompts.js 文件中,用于用户选择所需的插件。所有对话的答案都会传给generator的option
和 rootOptions
-
对话的配置项(所有配置项)
// prompts.js module.exports = [ { name: `addExampleRoutes`, // option的key type: 'confirm', // 选项类型:checkbox、confirm message: 'Add example routes?', // 对话显示的提示信息 default: false, // 默认值 validate: input => !!input, // 校验用户的答案,true则通过,否则返回错误提示字符串 } ]
参考**:**