问题描述
在使用 rspack-plugin-virtual-module 插件时,发现虚拟模块的动态更新功能(writeModule 方法)在开发环境下不会触发热更新,页面内容不会自动刷新。
问题复现
环境信息
- Rsbuild: v1.4.0
- rspack-plugin-virtual-module: v1.0.1
- Vue: v3.5.17
复现步骤
- 安装并配置
rspack-plugin-virtual-module - 在配置中使用
writeModule动态更新虚拟模块内容 - 启动开发服务器
- 观察到虚拟模块内容更新后,页面没有热更新
示例代码
// rsbuild.config.mjs
import { defineConfig } from '@rsbuild/core';
import { pluginVue } from '@rsbuild/plugin-vue';
import VirtualModulePlugin from 'rspack-plugin-virtual-module';
function rsbuildPlugin() {
return {
name: 'rsbuild-plugin',
setup(api) {
let virtualModulePluginInstance;
const virtualId = 'virtual-module';
api.modifyRspackConfig((rspackConfig) => {
virtualModulePluginInstance = new VirtualModulePlugin({
[virtualId]: 'export default "hello world";',
});
rspackConfig.plugins.push(virtualModulePluginInstance);
});
api.onAfterStartDevServer(() => {
setTimeout(() => {
if (virtualModulePluginInstance) {
// 这个更新不会触发热更新
virtualModulePluginInstance.writeModule(virtualId, 'export default "hello world2";');
}
}, 2000);
});
},
};
}
export default defineConfig({
plugins: [pluginVue(), rsbuildPlugin()],
});
问题原因分析
经过调试发现,rspack-plugin-virtual-module 插件会在 node_modules 目录下创建一个临时文件夹(格式为 rspack-virtual-module-xxxxxx),用于存储虚拟模块的实际文件。
核心问题:Rspack 默认会忽略 node_modules 目录下的所有文件变化,不会监听这些文件的修改,因此当虚拟模块内容更新时,不会触发重新编译和热更新。
问题机制
rspack-plugin-virtual-module创建临时目录:node_modules/rspack-virtual-module-020dbd51/- 虚拟模块文件存储在该目录下
writeModule方法修改该目录下的文件- Rspack 的文件监听忽略了
node_modules/**路径 - 文件变化未被检测到,热更新失效
解决方案
方案一:修改 watchOptions.ignored 配置(推荐)
在 Rspack 配置中,通过修改 watchOptions.ignored 来排除虚拟模块目录,使其能被文件监听系统检测到。
// rsbuild.config.mjs
import { defineConfig } from '@rsbuild/core';
import { pluginVue } from '@rsbuild/plugin-vue';
import VirtualModulePlugin from 'rspack-plugin-virtual-module';
function rsbuildPlugin() {
return {
name: 'rsbuild-plugin',
setup(api) {
let virtualModulePluginInstance;
const virtualId = 'virtual-module';
api.modifyRspackConfig((rspackConfig) => {
virtualModulePluginInstance = new VirtualModulePlugin({
[virtualId]: 'export default "hello world";',
});
rspackConfig.plugins.push(virtualModulePluginInstance);
// 🔑 关键配置:排除虚拟模块文件夹,使其能被监听
rspackConfig.watchOptions = {
...rspackConfig.watchOptions,
ignored: [
...(rspackConfig.watchOptions?.ignored || []),
'!**/node_modules/rspack-virtual-module-*/**', // 不忽略虚拟模块文件夹
],
};
});
api.onAfterStartDevServer(() => {
setTimeout(() => {
if (virtualModulePluginInstance) {
// 现在这个更新会触发热更新了!
virtualModulePluginInstance.writeModule(virtualId, 'export default "hello world2";');
}
}, 2000);
});
},
};
}
export default defineConfig({
plugins: [pluginVue(), rsbuildPlugin()],
});
方案二:使用 snapshot 配置
// 在 rspackConfig 中添加
rspackConfig.snapshot = {
...rspackConfig.snapshot,
managedPaths: [], // 清空默认的 node_modules 管理路径
immutablePaths: [], // 清空不可变路径
};
验证步骤
- 应用上述配置
- 启动开发服务器:
npm run dev - 在浏览器中打开应用
- 观察控制台输出和页面内容
- 等待 2 秒后,应该能看到:
- 虚拟模块内容从 "hello world" 更新为 "hello world2"
- 页面自动热更新,显示新内容
- 无需手动刷新页面
完整示例
项目结构
project/
├── src/
│ ├── App.vue
│ └── index.js
├── package.json
└── rsbuild.config.mjs
App.vue
<template>
<div class="app">
<h1>虚拟模块测试</h1>
<p>虚拟模块内容: {{ virtualContent }}</p>
</div>
</template>
<script>
import virtualModule from 'virtual-module'
export default {
name: 'App',
data() {
return {
virtualContent: virtualModule
}
}
}
</script>
package.json
{
"dependencies": {
"rspack-plugin-virtual-module": "^1.0.1",
"vue": "^3.5.17"
},
"devDependencies": {
"@rsbuild/core": "^1.3.22",
"@rsbuild/plugin-vue": "^1.0.7"
}
}
注意事项
- 通配符匹配:使用
rspack-virtual-module-*通配符来匹配所有可能的临时目录名称 - 感叹号语法:
!表示排除规则,即"不忽略"这些路径 - 性能影响:此配置可能会轻微影响构建性能,因为增加了对特定 node_modules 目录的监听
- 版本兼容性:此解决方案在 Rsbuild v1.4.0 和 rspack-plugin-virtual-module v1.0.1 下测试有效
相关链接
总结
这个问题的根本原因是 Rspack 默认忽略 node_modules 目录,而 rspack-plugin-virtual-module 在该目录下创建临时文件。通过配置 watchOptions.ignored 来排除虚拟模块目录,可以有效解决热更新失效的问题。
希望这个解决方案能帮助到遇到同样问题的开发者!