如何卸载脚本,让Vue组件每次挂载时重新加载?
问题:Module Federation远程组件重新挂载时无法重新加载脚本
需求是实现组件卸载后,再次挂载时能重新加载对应的远程脚本,但当前代码执行后,组件卸载再挂载并不会重新加载目标文件。
Vue组件代码
<template> <div id="root"> <component :is="App" /> </div> </template> <script setup> import { defineAsyncComponent, ref, onMounted, onUnmounted } from 'vue'; const App = ref(null); onMounted(() => { App.value = defineAsyncComponent(() => import('editor/App')); }); onUnmounted(() => { App.value = null; }); </script>
Webpack Module Federation配置
const { ModuleFederationPlugin } = require('webpack').container; module.exports = { configureWebpack: { plugins: [ new ModuleFederationPlugin({ name: 'consumer', filename: 'remoteEntry.js', remotes: { editor: process.env.FRONTEND_EDITOR_URI, }, }), ], }, };
解决思路
1. 清除Webpack模块缓存
Webpack会缓存所有异步加载的模块,即使把App设为null,下次import时还是会直接用缓存的模块,不会重新请求脚本。解决办法是在组件卸载时,手动清除对应模块的缓存:
修改Vue组件的onUnmounted钩子:
onUnmounted(() => { App.value = null; // 清除Webpack对远程组件的缓存 Object.keys(__webpack_module_cache__).forEach(key => { // 根据实际路径调整匹配规则 if (key.includes('editor/App')) { delete __webpack_module_cache__[key]; } }); // 可选:清除remoteEntry的缓存 const remoteEntryUrl = process.env.FRONTEND_EDITOR_URI.replace(/^editor@/, ''); Object.keys(__webpack_module_cache__).forEach(key => { if (key.includes(remoteEntryUrl)) { delete __webpack_module_cache__[key]; } }); });
这样下次onMounted里执行import('editor/App')时,Webpack会重新发起请求加载脚本。
2. 动态加载RemoteEntry脚本(更彻底的方案)
如果静态配置的remotes无法满足需求,可以改为动态加载remoteEntry,每次挂载组件时都重新加载远程入口脚本,确保拿到最新的资源:
<template> <div id="root"> <component :is="App" v-if="App" /> </div> </template> <script setup> import { ref, onMounted, onUnmounted } from 'vue'; const App = ref(null); const remoteEntryUrl = process.env.FRONTEND_EDITOR_URI.replace(/^editor@/, ''); let remoteScript = null; async function loadRemoteComponent() { // 清理旧资源和缓存 if (remoteScript) { remoteScript.remove(); remoteScript = null; delete window.editor; // 清除所有和editor相关的Webpack缓存 Object.keys(__webpack_module_cache__).forEach(key => { if (key.includes('editor')) { delete __webpack_module_cache__[key]; } }); } // 动态加载remoteEntry.js await new Promise((resolve, reject) => { remoteScript = document.createElement('script'); remoteScript.src = remoteEntryUrl; remoteScript.onload = resolve; remoteScript.onerror = reject; document.body.appendChild(remoteScript); }); // 初始化远程模块的共享作用域 await window.editor.init(__webpack_share_scopes__.default); // 加载远程组件 const module = await window.editor.get('./App'); App.value = module.default; } onMounted(() => { loadRemoteComponent(); }); onUnmounted(() => { App.value = null; if (remoteScript) { remoteScript.remove(); remoteScript = null; delete window.editor; Object.keys(__webpack_module_cache__).forEach(key => { if (key.includes('editor')) { delete __webpack_module_cache__[key]; } }); } }); </script>
这种方式完全绕开了静态remotes的缓存,每次挂载都重新拉取remoteEntry和组件脚本,适合需要强制刷新远程组件的场景。
3. 注意事项
__webpack_module_cache__是Webpack内部变量,不同版本可能有差异,测试时需确认当前版本兼容性。- 动态加载脚本时要添加错误处理,避免组件挂载异常。
- 如果远程组件有共享依赖,确保共享作用域初始化正确,避免依赖冲突。
内容的提问来源于stack exchange,提问作者Artem Ivanychev
相关产品推荐
相关产品推荐

