本文深入探讨 Vue CLI 插件开发的全过程,包含详细的工作原理、开发流程、实际案例和最佳实践,帮助您掌握自定义 Vue CLI 插件的核心技能。
1. Vue CLI 插件概述
1.1 什么是 Vue CLI 插件?
Vue CLI 插件是一个 npm 包,可以为 Vue CLI 创建的项目添加额外功能。它可以:
- 添加新的 CLI 命令
- 修改 webpack 配置
- 添加新的 UI 界面
- 注入额外的依赖
- 修改项目文件结构
- 集成第三方工具和服务
1.2 插件的作用和价值
解决的问题:
- 重复配置的自动化
- 团队工具链的统一
- 项目最佳实践的标准化
- 复杂功能的模块化封装
典型应用场景:
- UI 组件库集成(如 Element UI、Vant)
- 状态管理方案(如 Vuex、Pinia)
- 测试框架配置(如 Jest、Cypress)
- 微前端架构支持
- 部署和 DevOps 集成
2. Vue CLI 插件架构与工作原理
2.1 插件系统架构
graph TB
A[Vue CLI Service] --> B[Plugin API]
B --> C[Generator API]
B --> D[Service Plugin API]
B --> E[Prompt API]
C --> F[文件操作]
C --> G[依赖管理]
C --> H[模板渲染]
D --> I[Webpack 配置]
D --> J[ChainWebpack]
D --> K[环境变量]
E --> L[用户交互]
E --> M[条件逻辑]
F --> N[项目文件修改]
I --> O[构建配置]
2.2 核心概念解析
Service Plugin:
- 运行时插件
- 修改 webpack 配置
- 添加环境变量
- 注册 CLI 命令
Generator:
- 项目创建时执行
- 修改项目文件结构
- 注入依赖包
- 渲染模板文件
Prompts:
- 用户交互收集信息
- 条件性功能启用
- 动态配置生成
3. 插件开发环境搭建
3.1 创建插件项目结构
# 创建插件目录
mkdir vue-cli-plugin-my-plugin
cd vue-cli-plugin-my-plugin
# 初始化 package.json
npm init -y
项目结构:
vue-cli-plugin-my-plugin/
├── package.json
├── index.js # Service Plugin
├── generator.js # Generator API
├── prompts.js # 用户提示
├── ui.js # Vue CLI UI 集成
├── templates/ # 模板文件
│ ├── src/
│ │ ├── plugins/
│ │ │ └── my-plugin.js
│ │ └── views/
│ │ └── About.vue
│ └── tests/
│ └── e2e/
│ └── my-plugin.spec.js
└── README.md
3.2 基础 package.json 配置
{
"name": "vue-cli-plugin-my-plugin",
"version": "1.0.0",
"description": "A custom Vue CLI plugin for demo purposes",
"main": "index.js",
"keywords": [
"vue",
"vue-cli",
"plugin"
],
"author": "Your Name",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/your-username/vue-cli-plugin-my-plugin"
},
"engines": {
"node": ">=8.9",
"npm": ">=5.0.0"
},
"peerDependencies": {
"@vue/cli-service": "^3.0.0 || ^4.0.0 || ^5.0.0"
},
"devDependencies": {
"@vue/cli-service": "^5.0.0"
}
}
4. Service Plugin 开发
4.1 基础 Service Plugin 结构
// index.js
module.exports = (api, options) => {
// 检查 Vue CLI 版本
if (api.version && parseInt(api.version.split('.')[0]) < 3) {
throw new Error(
`vue-cli-plugin-my-plugin 需要 Vue CLI 3 或更高版本。当前版本: ${api.version}`
);
}
// 注册 CLI 命令
api.registerCommand(
'my-command',
{
description: '自定义插件命令演示',
usage: 'vue-cli-service my-command [options]',
options: {
'--mode': '指定环境模式 (默认: development)',
'--debug': '启用调试模式'
}
},
(args) => {
// 命令实现
require('./commands/my-command')(args, api, options);
}
);
// 修改 webpack 配置
api.chainWebpack((webpackConfig) => {
// 根据环境进行配置
if (process.env.NODE_ENV === 'production') {
configureForProduction(webpackConfig, options);
} else {
configureForDevelopment(webpackConfig, options);
}
});
// 配置开发服务器
api.configureDevServer((app, server) => {
// 添加自定义中间件
app.use('/api/my-plugin', require('./dev-server-middleware'));
});
// 监听文件变化
api.registerCommandHooks((hooks) => {
hooks.afterBuild.tap('my-plugin', (stats) => {
console.log('构建完成,执行插件后处理...');
});
});
};
// 生产环境配置
function configureForProduction(webpackConfig, options) {
webpackConfig
.plugin('my-plugin-banner')
.use(require('webpack').BannerPlugin, [
{
banner: `My Plugin v${require('./package.json').version}\nBuild time: ${new Date().toISOString()}`,
},
]);
}
// 开发环境配置
function configureForDevelopment(webpackConfig, options) {
webpackConfig
.devServer
.set('before', (app) => {
app.get('/__my-plugin__/status', (req, res) => {
res.json({ status: 'ok', timestamp: Date.now() });
});
});
}
4.2 自定义命令实现
// commands/my-command.js
module.exports = (args, api, options) => {
const chalk = require('chalk');
const { log, error, warn } = require('@vue/cli-shared-utils');
const fs = require('fs');
const path = require('path');
log(`${chalk.cyan('My Plugin Command')} - 开始执行...`);
try {
const projectRoot = api.resolve('.');
const packageJsonPath = path.join(projectRoot, 'package.json');
if (!fs.existsSync(packageJsonPath)) {
error('未找到 package.json 文件');
process.exit(1);
}
const packageJson = require(packageJsonPath);
// 显示项目信息
log(`项目名称: ${chalk.green(packageJson.name)}`);
log(`项目版本: ${chalk.green(packageJson.version)}`);
log(`Vue CLI 版本: ${chalk.green(api.version)}`);
// 根据参数执行不同操作
if (args.debug) {
log(`${chalk.yellow('调试模式已启用')}`);
log('参数:', args);
log('插件选项:', options.pluginOptions || {});
}
// 执行插件特定逻辑
if (args.mode) {
log(`运行模式: ${chalk.blue(args.mode)}`);
}
log(`${chalk.green('✓')} 命令执行完成`);
} catch (err) {
error(`命令执行失败: ${err.message}`);
process.exit(1);
}
};
5. Generator API 开发
5.1 Generator 核心实现
// generator.js
module.exports = (api, options, rootOptions) => {
// 插件选项
const pluginOptions = options.pluginOptions && options.pluginOptions.myPlugin
? options.pluginOptions.myPlugin
: {};
// 添加依赖
api.extendPackage({
dependencies: {
'axios': '^1.0.0',
'lodash': '^4.17.21'
},
devDependencies: {
'@types/lodash': '^4.14.182'
},
scripts: {
'my-plugin:analyze': 'vue-cli-service my-command --analyze',
'my-plugin:build': 'vue-cli-service my-command --mode production'
},
// 添加 ESLint 配置
eslintConfig: {
rules: {
'my-plugin/custom-rule': 'warn'
}
}
});
// 渲染模板文件
api.render('./templates', {
...options,
pluginOptions,
hasTypeScript: api.hasPlugin('typescript'),
hasRouter: api.hasPlugin('router'),
hasVuex: api.hasPlugin('vuex')
});
// 修改 main.js
api.injectImports(api.entryFile, `import './plugins/my-plugin'`);
// 条件性文件操作
if (pluginOptions.addExampleComponent) {
api.render({
'./src/components/ExampleComponent.vue': './templates/src/components/ExampleComponent.vue'
});
}
// 项目创建完成后的回调
api.onCreateComplete(() => {
const fs = require('fs');
const path = require('path');
const eslintrcPath = api.resolve('.eslintrc.js');
if (fs.existsSync(eslintrcPath)) {
// 修改 ESLint 配置
const content = fs.readFileSync(eslintrcPath, 'utf-8');
const updatedContent = content.replace(
'rules: {}',
`rules: {
'my-plugin/custom-rule': 'warn'
}`
);
fs.writeFileSync(eslintrcPath, updatedContent);
}
});
};
5.2 模板文件示例
插件入口文件:
// templates/src/plugins/my-plugin.js
import axios from 'axios';
import _ from 'lodash';
class MyPlugin {
constructor(options = {}) {
this.options = {
baseURL: process.env.VUE_APP_API_BASE_URL || '/api',
timeout: 10000,
...options
};
this.axiosInstance = axios.create({
baseURL: this.options.baseURL,
timeout: this.options.timeout
});
this.setupInterceptors();
this.installHelpers();
}
setupInterceptors() {
// 请求拦截器
this.axiosInstance.interceptors.request.use(
(config) => {
console.log(`[My Plugin] 发送请求: ${config.method?.toUpperCase()} ${config.url}`);
return config;
},
(error) => {
console.error('[My Plugin] 请求错误:', error);
return Promise.reject(error);
}
);
// 响应拦截器
this.axiosInstance.interceptors.response.use(
(response) => {
console.log(`[My Plugin] 收到响应: ${response.status} ${response.config.url}`);
return response;
},
(error) => {
console.error('[My Plugin] 响应错误:', error);
return Promise.reject(error);
}
);
}
installHelpers() {
// 添加全局工具方法
if (window && !window.$myPlugin) {
window.$myPlugin = {
deepClone: (obj) => _.cloneDeep(obj),
debounce: (func, wait) => _.debounce(func, wait),
request: this.axiosInstance
};
}
}
install(Vue) {
// 添加 Vue 实例方法
Vue.prototype.$myPlugin = this;
// 添加全局混入
Vue.mixin({
created() {
if (this.$options.myPluginOptions) {
console.log('[My Plugin] 组件创建:', this.$options.name);
}
}
});
// 添加全局组件
// Vue.component('MyPluginComponent', ...);
}
}
// 创建插件实例
const myPlugin = new MyPlugin();
export default myPlugin;
示例组件模板:
<!-- templates/src/components/ExampleComponent.vue -->
<template>
<div class="example-component">
<h3>{{ title }}</h3>
<div class="content">
<p>这是一个通过 My Plugin 自动生成的示例组件</p>
<button @click="handleClick" class="demo-button">
点击次数: {{ count }}
</button>
<div v-if="data" class="data-display">
<h4>示例数据:</h4>
<pre>{{ formattedData }}</pre>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'ExampleComponent',
data() {
return {
title: 'My Plugin 示例组件',
count: 0,
data: null
}
},
computed: {
formattedData() {
return this.data ? JSON.stringify(this.data, null, 2) : '暂无数据';
}
},
mounted() {
this.fetchData();
},
methods: {
handleClick() {
this.count++;
this.$emit('button-click', this.count);
},
async fetchData() {
try {
if (this.$myPlugin) {
const response = await this.$myPlugin.request.get('/example');
this.data = response.data;
}
} catch (error) {
console.error('获取数据失败:', error);
}
}
}
}
</script>
<style scoped>
.example-component {
border: 1px solid #eaeaea;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
background: #f9f9f9;
}
.example-component h3 {
color: #2c3e50;
margin-bottom: 15px;
}
.demo-button {
background-color: #3498db;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s;
}
.demo-button:hover {
background-color: #2980b9;
}
.data-display {
margin-top: 15px;
padding: 10px;
background: white;
border-radius: 4px;
border: 1px solid #ddd;
}
.data-display pre {
margin: 0;
font-size: 12px;
white-space: pre-wrap;
}
</style>
6. Prompts 用户交互
6.1 交互式提示配置
// prompts.js
module.exports = [
{
name: 'features',
type: 'checkbox',
message: '选择要启用的功能:',
choices: [
{
name: '添加示例组件',
value: 'addExampleComponent',
description: '添加一个演示用的 Vue 组件'
},
{
name: '集成 API 服务',
value: 'addApiService',
description: '添加 Axios 配置和 API 服务示例'
},
{
name: '添加工具函数',
value: 'addUtils',
description: '添加常用的工具函数库'
},
{
name: '集成代码规范',
value: 'addLinting',
description: '添加 ESLint 规则和代码规范配置'
}
],
default: ['addExampleComponent', 'addApiService']
},
{
name: 'apiBaseUrl',
type: 'input',
message: '请输入 API 基础 URL:',
default: '/api',
when: answers => answers.features.includes('addApiService'),
validate: input => input ? true : '请输入有效的 URL'
},
{
name: 'useTypeScript',
type: 'confirm',
message: '是否使用 TypeScript?',
default: false,
when: () => {
try {
require.resolve('typescript');
return true;
} catch (e) {
return false;
}
}
},
{
name: 'addDemoPage',
type: 'confirm',
message: '是否添加演示页面?',
default: true,
when: answers => answers.features.includes('addExampleComponent')
},
{
name: 'themeColor',
type: 'list',
message: '选择主题颜色:',
choices: [
{ name: '蓝色主题', value: 'blue' },
{ name: '绿色主题', value: 'green' },
{ name: '紫色主题', value: 'purple' },
{ name: '橙色主题', value: 'orange' }
],
default: 'blue',
when: answers => answers.features.includes('addExampleComponent')
}
];
6.2 基于用户选择的逻辑处理
// generator.js - 增强版本
module.exports = (api, options, rootOptions, answers = {}) => {
const pluginOptions = {
...(options.pluginOptions && options.pluginOptions.myPlugin),
...answers
};
// 根据用户选择添加依赖
const packageExtend = {
dependencies: {},
devDependencies: {},
scripts: {}
};
// 基础依赖
packageExtend.dependencies.axios = '^1.0.0';
// 条件依赖
if (pluginOptions.features && pluginOptions.features.includes('addUtils')) {
packageExtend.dependencies.lodash = '^4.17.21';
if (pluginOptions.useTypeScript) {
packageExtend.devDependencies['@types/lodash'] = '^4.14.182';
}
}
if (pluginOptions.features && pluginOptions.features.includes('addLinting')) {
packageExtend.devDependencies['eslint-plugin-my-plugin'] = '^1.0.0';
packageExtend.eslintConfig = {
extends: ['plugin:my-plugin/recommended']
};
}
api.extendPackage(packageExtend);
// 渲染模板
api.render('./templates', {
...options,
pluginOptions,
hasTypeScript: pluginOptions.useTypeScript || api.hasPlugin('typescript')
});
// 根据主题颜色修改配置
if (pluginOptions.themeColor) {
api.injectImports(api.entryFile, `import './styles/theme-${pluginOptions.themeColor}.css'`);
}
// 添加演示页面路由
if (pluginOptions.addDemoPage && api.hasPlugin('router')) {
api.injectImports(
api.resolve('./src/router/index.js'),
`import ExamplePage from '@/views/ExamplePage.vue'`
);
api.injectRootOptions(
api.resolve('./src/router/index.js'),
`routes: [
// 其他路由...
{
path: '/example',
name: 'ExamplePage',
component: ExamplePage
}
]`
);
}
};
7. UI 界面集成
7.1 Vue CLI UI 插件配置
// ui.js
const { openBrowser } = require('@vue/cli-shared-utils');
module.exports = (api, options) => {
// 添加任务
api.addTask({
name: 'my-plugin-task',
command: 'vue-cli-service my-command',
description: '运行 My Plugin 自定义任务',
link: 'https://github.com/your-username/vue-cli-plugin-my-plugin#readme'
});
// 添加配置面板
api.addClientAddon({
id: 'my-plugin.config',
url: 'https://unpkg.com/vue-cli-plugin-my-plugin/dist/config.js'
});
// 共享数据
api.onViewOpen((view) => {
if (view.id === 'my-plugin-view') {
console.log('My Plugin 视图已打开');
}
});
// 项目文件变化监听
api.onProjectFileChange((file) => {
if (file.endsWith('my-plugin.config.js')) {
api.emit('my-plugin-config-changed');
}
});
};
// 配置界面组件
module.exports.config = {
components: {
'my-plugin-config': {
template: `
<div class="my-plugin-config">
<h3>My Plugin 配置</h3>
<div class="config-section">
<div class="form-group">
<label>API 基础 URL</label>
<input
v-model="config.apiBaseUrl"
type="text"
placeholder="/api"
class="form-control"
>
</div>
<div class="form-group">
<label>
<input
v-model="config.enableDebug"
type="checkbox"
>
启用调试模式
</label>
</div>
<div class="form-group">
<label>主题颜色</label>
<select v-model="config.themeColor" class="form-control">
<option value="blue">蓝色</option>
<option value="green">绿色</option>
<option value="purple">紫色</option>
<option value="orange">橙色</option>
</select>
</div>
<button @click="saveConfig" class="btn btn-primary">
保存配置
</button>
</div>
</div>
`,
data() {
return {
config: {
apiBaseUrl: '/api',
enableDebug: false,
themeColor: 'blue'
}
};
},
methods: {
async saveConfig() {
try {
await this.$callPluginAction('my-plugin/save-config', this.config);
this.$notify({
title: '配置保存成功',
type: 'success'
});
} catch (error) {
this.$notify({
title: '保存失败',
text: error.message,
type: 'error'
});
}
}
},
async created() {
try {
const config = await this.$callPluginAction('my-plugin/load-config');
if (config) {
this.config = { ...this.config, ...config };
}
} catch (error) {
console.error('加载配置失败:', error);
}
}
}
}
};
8. 完整插件示例:Analytics Plugin
8.1 插件结构
vue-cli-plugin-analytics/
├── package.json
├── index.js
├── generator.js
├── prompts.js
├── commands/
│ └── analytics.js
├── templates/
│ ├── src/
│ │ ├── plugins/
│ │ │ └── analytics.js
│ │ └── components/
│ │ └── AnalyticsDashboard.vue
│ └── tests/
│ └── e2e/
│ └── analytics.spec.js
└── README.md
8.2 Service Plugin 实现
// index.js
module.exports = (api, options) => {
const analyticsOptions = options.pluginOptions && options.pluginOptions.analytics
? options.pluginOptions.analytics
: {};
// 注册分析命令
api.registerCommand(
'analytics',
{
description: '生成和分析项目数据',
usage: 'vue-cli-service analytics [options]',
options: {
'--report': '生成详细报告',
'--export [format]': '导出数据 (json|csv)',
'--watch': '监听文件变化'
}
},
(args) => {
require('./commands/analytics')(args, api, options);
}
);
// 添加环境变量
if (analyticsOptions.trackingId) {
process.env.VUE_APP_ANALYTICS_TRACKING_ID = analyticsOptions.trackingId;
}
// 修改 webpack 配置
api.chainWebpack((webpackConfig) => {
// 添加分析插件
if (process.env.NODE_ENV === 'production' && analyticsOptions.bundleAnalyzer) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
webpackConfig
.plugin('bundle-analyzer')
.use(BundleAnalyzerPlugin, [{
analyzerMode: 'static',
openAnalyzer: false
}]);
}
// 添加自定义 loader 用于分析
webpackConfig.module
.rule('analytics')
.test(/\.vue$/)
.pre()
.use('analytics')
.loader(require.resolve('./loaders/analytics-loader'))
.options(analyticsOptions);
});
// 开发服务器配置
api.configureDevServer((app) => {
app.get('/_analytics/data', (req, res) => {
res.json({
project: {
name: options.projectName,
version: require(api.resolve('package.json')).version
},
components: getComponentStats(api),
dependencies: getDependencyStats(api)
});
});
});
};
function getComponentStats(api) {
const fs = require('fs');
const path = require('path');
const componentsDir = api.resolve('./src/components');
let components = [];
if (fs.existsSync(componentsDir)) {
components = fs.readdirSync(componentsDir)
.filter(file => file.endsWith('.vue'))
.map(file => {
const filePath = path.join(componentsDir, file);
const stats = fs.statSync(filePath);
const content = fs.readFileSync(filePath, 'utf-8');
return {
name: file.replace('.vue', ''),
size: stats.size,
lines: content.split('\n').length,
hasScript: content.includes('<script'),
hasStyle: content.includes('<style')
};
});
}
return components;
}
function getDependencyStats(api) {
const packageJson = require(api.resolve('package.json'));
return {
dependencies: Object.keys(packageJson.dependencies || {}).length,
devDependencies: Object.keys(packageJson.devDependencies || {}).length,
total: Object.keys(packageJson.dependencies || {}).length +
Object.keys(packageJson.devDependencies || {}).length
};
}
8.3 分析命令实现
// commands/analytics.js
const chalk = require('chalk');
const { log, error, warn, info } = require('@vue/cli-shared-utils');
const fs = require('fs');
const path = require('path');
module.exports = (args, api, options) => {
const projectRoot = api.resolve('.');
log(`${chalk.cyan('Vue CLI Analytics')} - 项目分析工具\n`);
try {
// 收集项目数据
const projectData = collectProjectData(projectRoot);
// 显示基础信息
displayBasicInfo(projectData);
// 生成报告
if (args.report) {
generateReport(projectData, args);
}
// 导出数据
if (args.export) {
exportData(projectData, args.export);
}
} catch (err) {
error(`分析失败: ${err.message}`);
process.exit(1);
}
};
function collectProjectData(projectRoot) {
const packageJson = require(path.join(projectRoot, 'package.json'));
// 收集组件信息
const components = collectComponents(projectRoot);
// 收集路由信息
const routes = collectRoutes(projectRoot);
// 收集依赖信息
const dependencies = collectDependencies(packageJson);
return {
project: {
name: packageJson.name,
version: packageJson.version,
description: packageJson.description
},
components,
routes,
dependencies,
stats: {
totalComponents: components.length,
totalRoutes: routes.length,
totalDependencies: dependencies.total
}
};
}
function collectComponents(projectRoot) {
const componentsDir = path.join(projectRoot, 'src/components');
const components = [];
if (fs.existsSync(componentsDir)) {
const walk = (dir) => {
const files = fs.readdirSync(dir);
files.forEach(file => {
const filePath = path.join(dir, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
walk(filePath);
} else if (file.endsWith('.vue')) {
const content = fs.readFileSync(filePath, 'utf-8');
const lines = content.split('\n').length;
components.push({
name: file.replace('.vue', ''),
path: path.relative(projectRoot, filePath),
size: stat.size,
lines,
hasScript: content.includes('<script'),
hasStyle: content.includes('<style'),
hasTemplate: content.includes('<template')
});
}
});
};
walk(componentsDir);
}
return components;
}
function displayBasicInfo(data) {
info(`项目: ${chalk.green(data.project.name)} v${data.project.version}`);
info(`组件数量: ${chalk.blue(data.stats.totalComponents)}`);
info(`路由数量: ${chalk.blue(data.stats.totalRoutes)}`);
info(`依赖数量: ${chalk.blue(data.stats.totalDependencies)}`);
// 显示最大的组件
const largestComponent = data.components
.sort((a, b) => b.size - a.size)[0];
if (largestComponent) {
warn(`最大的组件: ${chalk.yellow(largestComponent.name)} (${(largestComponent.size / 1024).toFixed(2)} KB)`);
}
}
9. 插件测试和调试
9.1 本地测试配置
// package.json - 测试脚本
{
"scripts": {
"test:unit": "jest",
"test:e2e": "cypress run",
"test:integration": "node test/integration.js",
"dev": "node test/dev-server.js",
"link:local": "npm link && cd test-project && npm link vue-cli-plugin-my-plugin"
},
"devDependencies": {
"jest": "^27.0.0",
"cypress": "^9.0.0",
"@vue/test-utils": "^2.0.0"
}
}
9.2 集成测试示例
// test/integration.test.js
const { runCLI } = require('@vue/cli-test-utils');
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
describe('vue-cli-plugin-my-plugin', () => {
const projectName = 'test-project';
const projectPath = path.join(__dirname, '..', projectName);
beforeAll(async () => {
// 创建测试项目
await runCLI(['create', projectName, '--preset', 'default'], {
cwd: path.join(__dirname, '..')
});
});
afterAll(() => {
// 清理测试项目
if (fs.existsSync(projectPath)) {
fs.rmSync(projectPath, { recursive: true });
}
});
test('插件安装成功', async () => {
// 安装插件
await runCLI(['add', 'vue-cli-plugin-my-plugin'], {
cwd: projectPath
});
// 检查文件是否生成
const pluginFile = path.join(projectPath, 'src/plugins/my-plugin.js');
expect(fs.existsSync(pluginFile)).toBe(true);
// 检查 package.json 是否更新
const packageJson = require(path.join(projectPath, 'package.json'));
expect(packageJson.dependencies).toHaveProperty('axios');
});
test('自定义命令工作正常', async () => {
const result = await runCLI(['my-command'], {
cwd: projectPath
});
expect(result.stdout).toContain('My Plugin Command');
});
});
10. 插件发布和维护
10.1 发布准备
// package.json - 发布配置
{
"name": "vue-cli-plugin-my-plugin",
"version": "1.0.0",
"description": "A feature-rich Vue CLI plugin for analytics and project management",
"keywords": [
"vue",
"vue-cli",
"plugin",
"analytics",
"project-management"
],
"homepage": "https://github.com/your-username/vue-cli-plugin-my-plugin#readme",
"bugs": {
"url": "https://github.com/your-username/vue-cli-plugin-my-plugin/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/your-username/vue-cli-plugin-my-plugin.git"
},
"license": "MIT",
"author": "Your Name <your.email@example.com>",
"files": [
"index.js",
"generator.js",
"prompts.js",
"ui.js",
"commands",
"templates",
"README.md"
],
"peerDependencies": {
"@vue/cli-service": "^3.0.0 || ^4.0.0 || ^5.0.0"
},
"devDependencies": {
"@vue/cli-service": "^5.0.0",
"jest": "^27.0.0"
},
"engines": {
"node": ">=12.0.0"
}
}
10.2 文档和示例
# Vue CLI Plugin My Plugin
一个功能丰富的 Vue CLI 插件,提供项目分析、工具集成和开发效率提升功能。
## 功能特性
- 📊 项目分析和统计
- 🔧 自动化工具配置
- 🎨 主题和样式管理
- 📈 性能监控和优化
- 🔌 可扩展的插件架构
## 安装
```bash
vue add my-plugin
配置
在 vue.config.js 中配置插件选项:
module.exports = {
pluginOptions: {
myPlugin: {
apiBaseUrl: '/api',
enableDebug: true,
themeColor: 'blue'
}
}
}
使用
命令行使用
# 运行分析
vue-cli-service analytics --report
# 导出数据
vue-cli-service analytics --export json
# 自定义命令
vue-cli-service my-command --debug
在代码中使用
// 在 Vue 组件中
export default {
mounted() {
if (this.$myPlugin) {
this.$myPlugin.request.get('/data')
.then(response => {
console.log('Data:', response.data);
});
}
}
}
API 参考
Plugin Options
apiBaseUrl(String): API 基础 URLenableDebug(Boolean): 启用调试模式themeColor(String): 主题颜色
Global Methods
$myPlugin.request: Axios 实例$myPlugin.deepClone: 深度克隆工具$myPlugin.debounce: 防抖函数
许可证
MIT
## 11. 总结
通过本文的详细讲解,您应该已经掌握了:
1. **Vue CLI 插件架构**:理解 Service Plugin、Generator、Prompts 的核心概念
2. **插件开发流程**:从环境搭建到发布维护的完整流程
3. **高级功能实现**:UI 集成、自定义命令、模板渲染等
4. **实战经验**:通过完整示例学习实际开发技巧
5. **最佳实践**:测试、调试、文档和维护的最佳方法
Vue CLI 插件生态系统为 Vue.js 开发提供了强大的扩展能力,掌握插件开发技能将极大提升您的开发效率和项目质量。
---
**扩展学习资源:**
- [Vue CLI 官方文档](https://cli.vuejs.org/)
- [Plugin Development Guide](https://cli.vuejs.org/dev-guide/plugin-dev.html)
- [Webpack Configuration](https://webpack.js.org/configuration/)
- [Node.js API Documentation](https://nodejs.org/api/)
开始创建您自己的 Vue CLI 插件,为 Vue.js 生态系统贡献力量!