一个完整的VUE3插件代码实例
软件老师 罗光宣
这段代码构建了一个基于Vue 3的动态插件系统,其核心功能是允许用户动态启用和停用插件,并且支持插件之间的事件通信。以下是对代码的详细说明:
整体架构
整个系统包含多个插件,如 analytics(数据分析)和 logger(系统日志),每个插件有自己的功能模块和生命周期方法。系统通过插件管理器来加载、激活和停用这些插件,并使用事件总线来实现插件间的通信。
主要文件及功能介绍
1. 目录结构:
src/├── plugins/│├── analytics/││├── index.js││└── analytics.vue││── logger/││├── index.js││└── logger.vueApp││── exApp││├── pluginLoader.js││├── pluginBus.js││└── App_Ex_Plugins.vue
2. plugins/exApp/App_Ex_Plugins.vue
·模板部分:
o提供了一个用户界面,包含插件启用/禁用按钮和插件显示区域。
o有一个“发送测试事件”按钮,用于触发事件。
·脚本部分:
o使用 availablePlugins 存储可用插件列表,activePlugins 存储当前激活的插件。
otogglePlugin 方法用于动态加载和卸载插件,通过 loadPlugin 函数实现。
osendTestEvent 方法用于发送 log 和 order-created 事件。
<template><divclass="app"><h1>动态插件系统</h1><divclass="button-group"><buttonv-for="plugin in availablePlugins":key="plugin"@click="togglePlugin(plugin)":class="{ active: activePlugins.includes(plugin) }">{{ activePlugins.includes(plugin) ? '禁用' : '启用' }} {{ plugin }}</button></div><divclass="plugin-container"><templatev-if="loading">加载插件中...</template><templatev-else><componentv-for="plugin in activeComponents":is="plugin.component":key="plugin.name"/></template></div><button @click="sendTestEvent">发送测试事件</button></div></template><scriptsetup>import { ref, shallowRef } from 'vue'import { loadPlugin } from './pluginLoader'import { usePluginBus } from './pluginBus'const bus = usePluginBus()// 可用插件列表const availablePlugins = ['analytics', 'logger']const activePlugins = ref([])const activeComponents = shallowRef([])const loading = ref(false)const togglePlugin = async (pluginName) => {loading.value = truetry {if (activePlugins.value.includes(pluginName)) {// 停用插件activePlugins.value = activePlugins.value.filter(name => name !== pluginName)activeComponents.value = activeComponents.value.filter(p => p.name !== pluginName)} else {// 激活插件const plugin = await loadPlugin(pluginName)activePlugins.value.push(pluginName)activeComponents.value = [...activeComponents.value,{name: pluginName,component: plugin.component}]await plugin.install?.()}} catch (err) {console.error(`插件 ${pluginName} 操作失败:`, err)} finally {loading.value = false}}// 发送测试事件const sendTestEvent = () => {bus.emit('log', '用户点击了测试按钮')bus.emit('order-created', { id: 123, amount: 99.9 })}</script><style>.app {font-family: Arial, sans-serif;max-width: 800px;margin: 0 auto;padding: 20px;}.button-group {margin: 20px 0;display: flex;gap: 10px;}button {padding: 8px 16px;background: #f0f0f0;border: 1px solid #ddd;border-radius: 4px;cursor: pointer;}button.active {background: #e0e0ff;border-color: #a0a0ff;}.plugin-container {min-height: 100px;padding: 15px;border: 1px solid #eee;border-radius: 4px;}</style>
3. plugins/exApp/pluginLoader.js
·loadPlugin 函数:
o动态导入插件的 index.js 文件和对应的 .vue 组件。
o使用 Map 缓存插件定义,避免重复加载。
import { defineAsyncComponent } from 'vue'const plugins = new Map()export async function loadPlugin(pluginName) {if (plugins.has(pluginName)) {return plugins.get(pluginName)}try {// 动态导入插件逻辑(修复路径错误)const pluginModule = await import(/* @vite-ignore */`../${pluginName}/index.js`)// 缓存插件定义plugins.set(pluginName, {name: pluginName,component: defineAsyncComponent(() =>import(`../${pluginName}/${pluginName}.vue`)),install: pluginModule.install,uninstall: pluginModule.uninstall})return plugins.get(pluginName)} catch (err) {console.error(`加载插件 ${pluginName} 失败:`, err)throw err}}
4. plugins/analytics 文件夹
·analytics.vue:
o展示今日访问量和转化率数据。
o监听 order-created 事件,当事件触发时打印订单信息。
<template><divclass="analytics-panel"><h3>数据分析面板</h3><divv-if="data"><p>今日访问量: {{ data.visits }}</p><p>转化率: {{ data.conversion }}%</p></div></div></template><scriptsetup>import { ref, onMounted } from 'vue'import { usePluginBus } from '../exApp/pluginBus'const data = ref(null)const bus = usePluginBus()onMounted(() => {// 模拟数据加载setTimeout(() => {data.value = {visits: 1243,conversion: 3.2}}, 500)// 监听其他插件事件bus.on('order-created', (order) => {console.log('订单创建事件:', order)})})</script>
·index.js:
o定义了 analytics 插件,包含安装和卸载方法。
o在安装时,向应用程序提供 analytics 服务,用于追踪事件。
//import Analytics from './Analytics.vue'export default {name: 'analytics',component: () => import('./Analytics.vue'), // ✅ 正确:动态导入install(app, options) {console.log('Analytics插件安装', options)// 注册全局服务app.provide('analytics', {trackEvent: (event) => {console.log('追踪事件:', event)}})},uninstall() {console.log('Analytics插件卸载')}}
5. plugins/logger 文件夹
·logger.vue:
o显示系统日志列表。
o监听 log 事件,将日志信息添加到列表中。
<template><divclass="logger-panel"><h3>系统日志</h3><ul><liv-for="(log, index) in logs":key="index">[{{ log.time }}] {{ log.message }}</li></ul></div></template><scriptsetup>import { ref } from 'vue'import { usePluginBus } from '../exApp/pluginBus'const logs = ref([])const bus = usePluginBus()// 监听全局日志事件bus.on('log', (message) => {logs.value.push({time: new Date().toLocaleTimeString(),message})})</script>
– **`index.js`**:– 定义了 `logger` 插件,包含安装和卸载方法。– 在安装时,向应用程序全局添加 `$log` 方法。
export default {name: 'logger',component: () => import('./Logger.vue'), // 改为动态导入install(app) {console.log('Logger插件安装')app.config.globalProperties.$log = (message) => {console.log(message)}},uninstall() {console.log('Logger插件卸载')}}
事件通信:plugins/exApp/usePluginBus.js
·使用 usePluginBus 创建的事件总线实现插件间的通信。
·App_Ex_Plugins.vue 的 sendTestEvent 方法发送 log 和 order-created 事件。
·analytics.vue 监听 order-created 事件,logger.vue 监听 log 事件。
import { ref } from 'vue'const listeners = ref({})export const usePluginBus = () => {// 监听事件const on = (event, callback) => {if (!listeners.value[event]) {listeners.value[event] = []}listeners.value[event].push(callback)}// 触发事件const emit = (event, ...args) => {if (listeners.value[event]) {listeners.value[event].forEach(cb => cb(...args))}}// 移除监听const off = (event, callback) => {if (listeners.value[event]) {listeners.value[event] = listeners.value[event].filter(cb => cb !== callback)}}return {on,emit,off}}
动态加载
·使用 defineAsyncComponent 和 import() 动态加载插件组件,实现按需加载。
插件生命周期
·每个插件通过 install 和 uninstall 方法管理其生命周期,确保在启用和停用插件时执行相应的操作。
通过这种方式,系统实现了插件的动态加载、管理和通信,提高了系统的可扩展性和灵活性。
运行情况
(1)初始运行界面

(2)点击加载Analytics插件后的运行界面

(3)点击加载Analytics与logger插件,再点击“发送测试事件”按钮后的运行界面

夜雨聆风
