From d3f9c04209a3f43e4debe601bdb3313df21df544 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Wed, 7 May 2025 08:21:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=8F=92=E4=BB=B6=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E6=96=87=E6=A1=A3=EF=BC=8C=E8=B0=83=E6=95=B4=E5=A4=9A?= =?UTF-8?q?=E4=B8=AA=E7=BB=84=E4=BB=B6=E4=BB=A5=E6=94=AF=E6=8C=81=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=A2=9E=E5=BC=BA=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E4=BA=A4=E4=BA=92=E4=BD=93=E9=AA=8C=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E9=85=8D=E7=BD=AE=E7=A4=BA=E4=BE=8B=E4=BB=A5?= =?UTF-8?q?=E5=8F=8D=E6=98=A0=E6=9C=80=E6=96=B0=E7=9A=84=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E7=BB=93=E6=9E=84=E5=92=8C=E4=BE=9D=E8=B5=96=E5=85=B3=E7=B3=BB?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/federation-troubleshooting.md | 85 +-------- docs/module-federation-guide.md | 81 ++++---- examples/plugin-component/README.md | 174 +----------------- .../src/components/Config.vue | 12 +- .../plugin-component/src/components/Page.vue | 19 +- examples/plugin-component/vite.config.js | 7 +- src/components/dialog/PluginConfigDialog.vue | 1 + src/components/dialog/PluginDataDialog.vue | 2 +- 8 files changed, 85 insertions(+), 296 deletions(-) diff --git a/docs/federation-troubleshooting.md b/docs/federation-troubleshooting.md index 765c16cf..d0cfd0b7 100644 --- a/docs/federation-troubleshooting.md +++ b/docs/federation-troubleshooting.md @@ -2,8 +2,6 @@ 本文档提供了针对 MoviePilot 项目中使用模块联邦时可能遇到的常见问题及解决方案。 -关联阅读后端插件开发文档:[第三方插件开发说明](https://github.com/jxxghp/MoviePilot-Plugins/blob/main/README.md) - ## 远程组件注册机制 MoviePilot 使用自动注册机制来加载远程组件: @@ -104,90 +102,9 @@ localStorage.setItem('debug', 'vite:*') 创建一个独立的简单页面来测试插件组件,排除主应用的干扰因素。 -## 最佳实践 - -### 插件组件项目配置 - -```js -// vite.config.js -import { defineConfig } from 'vite' -import vue from '@vitejs/plugin-vue' -import federation from '@originjs/vite-plugin-federation' - -export default defineConfig({ - plugins: [ - vue(), - federation({ - name: 'remoteApp', - filename: 'remoteEntry.js', - exposes: { - './PluginComponent': './src/App.vue', - }, - shared: { - vue: { - singleton: true, - requiredVersion: false - } - } - }) - ], - build: { - target: 'esnext', - minify: false, // 开发阶段禁用最小化,方便调试 - cssCodeSplit: false, - rollupOptions: { - output: { - minifyInternalExports: false - } - } - } -}) -``` - -### 插件组件代码 - -```vue - - - -``` - -### 显式依赖声明 - -在插件组件的 `package.json` 中准确声明所有依赖: - -```json -{ - "dependencies": { - "vue": "^3.3.4" - }, - "devDependencies": { - "@originjs/vite-plugin-federation": "^1.3.5", - "@vitejs/plugin-vue": "^4.4.0", - "vite": "^5.0.0" - } -} -``` - ## 其他资源 +- [MoviePilot 插件组件示例](../examples/plugin-component/) - [Vite 模块联邦插件文档](https://github.com/originjs/vite-plugin-federation) - [Vite 官方文档](https://vitejs.dev/guide/build.html) - [Origin.js 模块联邦示例](https://github.com/originjs/vite-plugin-federation/tree/main/packages/examples) -- [MoviePilot 插件组件示例](../examples/plugin-component/) diff --git a/docs/module-federation-guide.md b/docs/module-federation-guide.md index 64539021..7ada5082 100644 --- a/docs/module-federation-guide.md +++ b/docs/module-federation-guide.md @@ -4,6 +4,9 @@ MoviePilot前端采用模块联邦(Module Federation)技术实现插件的动态加载和集成。本文档详细说明如何开发符合要求的远程模块,以便在MoviePilot中作为插件使用。 +关联阅读后端插件开发文档:[第三方插件开发说明](https://github.com/jxxghp/MoviePilot-Plugins/blob/main/README.md) + + ## 2. 技术要求 - Node.js 16+ @@ -33,13 +36,7 @@ npm create vite@latest my-plugin -- --template vue-ts cd my-plugin # 安装依赖 -npm install - -# 安装模块联邦插件 -npm install @originjs/vite-plugin-federation --save-dev - -# 安装Vuetify(可选) -npm install vuetify +yarn ``` ### 配置vite.config.ts @@ -53,31 +50,27 @@ export default defineConfig({ plugins: [ vue(), federation({ - name: 'my_plugin', // 插件名称,建议与插件ID保持一致 + name: 'LogsClean', filename: 'remoteEntry.js', exposes: { './Page': './src/components/Page.vue', './Config': './src/components/Config.vue', './Dashboard': './src/components/Dashboard.vue', }, - shared: { - vue: { requiredVersion: false }, - vuetify: { requiredVersion: false } - } + shared: ['vue', 'vuetify'], + format: 'esm' }) ], build: { target: 'esnext', // 必须设置为esnext以支持顶层await minify: false, // 开发阶段建议关闭混淆 cssCodeSplit: false, - rollupOptions: { - output: { - format: 'esm', // 必须使用ESM格式 - entryFileNames: '[name].js', - chunkFileNames: '[name].js', - } - } - } + }, + server: { + port: 5001, // 使用不同于主应用的端口 + cors: true, // 启用CORS + origin: 'http://localhost:5001' + }, }) ``` @@ -88,7 +81,15 @@ export default defineConfig({ ```vue -``` - -### Config.vue(配置页面) - -配置页面用于接收和保存插件配置: - -- 接收 `initialConfig` 属性获取初始配置 -- 发出 `save` 事件保存配置数据 - -```vue - -``` - -### Dashboard.vue(仪表板组件) - -仪表板组件用于在主页上显示插件数据: - -- 接收 `config` 属性获取仪表板配置 -- 接收 `allowRefresh` 属性控制是否允许自动刷新 - -```vue - -``` - -## 5. 构建和部署 - -### 构建生产版本 - -```bash -npm run build -# 或 -yarn build -``` - -构建后在 `dist` 目录生成以下关键文件: - -- `remoteEntry.js` - 模块联邦入口文件 -- `Page.js` - 详情页面组件 -- `Config.js` - 配置页面组件 -- `Dashboard.js` - 仪表板组件 - -### 部署到插件后端 - -将构建后的文件部署到插件后端插件目录下,并上传到Github仓库。 - -## 6. 插件后端集成 - -在插件的后端代码中,实现以下方法来集成远程组件: - -```python -def get_render_mode() -> Tuple[str, str]: - """ - 获取插件渲染模式 - :return: 1、渲染模式,支持:vue/vuetify,默认vuetify - :return: 2、组件路径,默认 dist/assets - """ - return "vue", "dist/assets" -``` - -## 7. 常见问题排查 - -### 模块加载问题 - -如果遇到模块加载问题,请检查: - -1. 确保 `build.target` 设置为 `esnext` -2. 验证共享依赖配置是否正确 -3. 检查网络请求是否成功 -4. 查看浏览器控制台错误信息 - -### 代码调试 - -在开发阶段可以: - -1. 使用浏览器开发者工具进行调试 -2. 启用 Vite 的详细日志:`localStorage.setItem('debug', 'vite:*')` -3. 使用 `console.log` 输出调试信息 - -更多详细说明请参考 [模块联邦开发指南](../../docs/module-federation-guide.md) 和 [模块联邦问题排查指南](../../docs/federation-troubleshooting.md)。 +- [模块联邦开发指南](../../docs/module-federation-guide.md) +- [模块联邦问题排查指南](../../docs/federation-troubleshooting.md)。 diff --git a/examples/plugin-component/src/components/Config.vue b/examples/plugin-component/src/components/Config.vue index 7415793c..9cb31b56 100644 --- a/examples/plugin-component/src/components/Config.vue +++ b/examples/plugin-component/src/components/Config.vue @@ -99,6 +99,7 @@ 重置 保存配置 + 关闭 @@ -113,6 +114,10 @@ const props = defineProps({ type: Object, default: () => ({}), }, + api: { + type: any, + default: () => {}, + }, }) // 表单状态 @@ -162,7 +167,7 @@ onMounted(() => { }) // 自定义事件,用于保存配置 -const emit = defineEmits(['save']) +const emit = defineEmits(['save', 'close', 'switch']) // 保存配置 async function saveConfig() { @@ -198,6 +203,11 @@ function resetForm() { form.value.resetValidation() } } + +// 通知主应用关闭组件 +function notifyClose() { + emit('close') +}