WP5 模块联盟:remoteEntry.js 缓存
WP5 Module Federation: remoteEntry.js caching
使用 Webpack 5 模块联合,如果修改了远程入口,则无需重新部署 main module/application 并且会在浏览器请求时加载最新版本的模块。
我想知道:由于远程 URL 保持不变(例如 http://localhost:8081/remoteEntry.js
),浏览器可能会缓存文件和每次加载主模块时加载的缓存版本。另一方面,如果您为远程条目添加缓存破坏,您将没有缓存。
让我们假设有一个使用 Webpack 5 模块联合的微前端架构的应用程序。有一个远程微前端,其配置如下:
output: {
publicPath: "http://localhost:8081/",
},
plugins: [
new ModuleFederationPlugin({
name: "app1",
filename: "remoteEntry.js",
exposes: {
"./Component1": "./src/Component1",
"./someModule1": "./src/someModule1",
},
})
]
然后是主模块配置:
output: {
publicPath: "http://localhost:8080/",
},
plugins: [
new ModuleFederationPlugin({
name: "mainApp",
filename: "remoteEntry.js",
remotes: {
app1: "app1@http://localhost:8081/remoteEntry.js"
}
})
]
两个模块都已部署到生产环境中。
然后我将 app1
更改为 Component1
并部署 app1
模块。
如何处理远程模块缓存?
更新:
在我的例子中,浏览器似乎对 remoteEntry.js
使用了启发式缓存 (https://www.rfc-editor.org/rfc/rfc7234#section-4.2.2),因为服务器没有提供明确的过期时间。
因此,当remoteEntry.js
更新时,主应用程序仍然从可以缓存数周的缓存中加载该文件。
对于块,这不是问题,因为可以将 webpack 配置为在文件名中包含哈希。
对于 remoteEntry.js
我看到了 2 个选项:
- 缓存破坏
- 明确指定缓存控制
你觉得可行吗?
我认为这不是问题,因为 webpack 会在构建过程中向应用程序的 js 文件添加缓存破坏。查看加载应用程序时返回的 HTML 代码,您会注意到每次部署后加载捆绑包的标签都不同。 remoteEntry.js
是允许您的应用程序从远程服务器加载的模块联合方式,而不是您的实际应用程序代码。
我参加派对可能有点晚了,但这里有一个解决方案,您可以在构建时在 remoteEntry 的末尾附加一个随机字符串。
https://github.com/module-federation/module-federation-examples/issues/566#issue-785273439
缓存破坏意味着重新构建(或者至少,post-处理)主应用程序包,这是模块联合试图避免的问题之一。
因此,考虑到 remoteEntry.js
通常是一个小文件,最好的解决方案是对该文件应用特定的缓存控制,使其永远不会被缓存。
有了nginx,就可以这样做:
location ~ .*remoteEntry.js$ {
expires -1;
add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
}
在我看来,您永远不应该缓存 remoteEntry.js
,因为那是块映射所在的位置。 (这里的映射是指组件和它们的块 url 之间的映射)。您总是希望确保显示的是最新的远程块。如果映射发生变化(通常只是意味着远程组件更新),浏览器应该获取新块。
也就是说,您应该使用
缓存破坏块
output: {
filename: '[name].[contenthash].js',
},
plugins: [
new container.ModuleFederationPlugin({
name: "RemoteModule",
library: { type: "var", name: "RemoteModule" },
filename: "remoteEntry.js",
exposes: {
'./SuperButton': "./src/components/SuperButton",
'./SuperButton2': "./src/components/SuperButton2",
},
shared: {
react: { singleton: true, eager: true },
"react-dom": { singleton: true, eager: true },
}
}),
new HtmlWebpackPlugin({
template: "./public/index.html"
})
],
在你webpack.config.js
这里推荐https://webpack.js.org/guides/caching/
这样,主机将始终尝试获取 remoteEntry.js
(没有哈希的固定 url)并让浏览器获取新块 url如果更改哈希值。
使用 Webpack 5 模块联合,如果修改了远程入口,则无需重新部署 main module/application 并且会在浏览器请求时加载最新版本的模块。
我想知道:由于远程 URL 保持不变(例如 http://localhost:8081/remoteEntry.js
),浏览器可能会缓存文件和每次加载主模块时加载的缓存版本。另一方面,如果您为远程条目添加缓存破坏,您将没有缓存。
让我们假设有一个使用 Webpack 5 模块联合的微前端架构的应用程序。有一个远程微前端,其配置如下:
output: {
publicPath: "http://localhost:8081/",
},
plugins: [
new ModuleFederationPlugin({
name: "app1",
filename: "remoteEntry.js",
exposes: {
"./Component1": "./src/Component1",
"./someModule1": "./src/someModule1",
},
})
]
然后是主模块配置:
output: {
publicPath: "http://localhost:8080/",
},
plugins: [
new ModuleFederationPlugin({
name: "mainApp",
filename: "remoteEntry.js",
remotes: {
app1: "app1@http://localhost:8081/remoteEntry.js"
}
})
]
两个模块都已部署到生产环境中。
然后我将 app1
更改为 Component1
并部署 app1
模块。
如何处理远程模块缓存?
更新:
在我的例子中,浏览器似乎对 remoteEntry.js
使用了启发式缓存 (https://www.rfc-editor.org/rfc/rfc7234#section-4.2.2),因为服务器没有提供明确的过期时间。
因此,当remoteEntry.js
更新时,主应用程序仍然从可以缓存数周的缓存中加载该文件。
对于块,这不是问题,因为可以将 webpack 配置为在文件名中包含哈希。
对于 remoteEntry.js
我看到了 2 个选项:
- 缓存破坏
- 明确指定缓存控制
你觉得可行吗?
我认为这不是问题,因为 webpack 会在构建过程中向应用程序的 js 文件添加缓存破坏。查看加载应用程序时返回的 HTML 代码,您会注意到每次部署后加载捆绑包的标签都不同。 remoteEntry.js
是允许您的应用程序从远程服务器加载的模块联合方式,而不是您的实际应用程序代码。
我参加派对可能有点晚了,但这里有一个解决方案,您可以在构建时在 remoteEntry 的末尾附加一个随机字符串。 https://github.com/module-federation/module-federation-examples/issues/566#issue-785273439
缓存破坏意味着重新构建(或者至少,post-处理)主应用程序包,这是模块联合试图避免的问题之一。
因此,考虑到 remoteEntry.js
通常是一个小文件,最好的解决方案是对该文件应用特定的缓存控制,使其永远不会被缓存。
有了nginx,就可以这样做:
location ~ .*remoteEntry.js$ {
expires -1;
add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
}
在我看来,您永远不应该缓存 remoteEntry.js
,因为那是块映射所在的位置。 (这里的映射是指组件和它们的块 url 之间的映射)。您总是希望确保显示的是最新的远程块。如果映射发生变化(通常只是意味着远程组件更新),浏览器应该获取新块。
也就是说,您应该使用
缓存破坏块 output: {
filename: '[name].[contenthash].js',
},
plugins: [
new container.ModuleFederationPlugin({
name: "RemoteModule",
library: { type: "var", name: "RemoteModule" },
filename: "remoteEntry.js",
exposes: {
'./SuperButton': "./src/components/SuperButton",
'./SuperButton2': "./src/components/SuperButton2",
},
shared: {
react: { singleton: true, eager: true },
"react-dom": { singleton: true, eager: true },
}
}),
new HtmlWebpackPlugin({
template: "./public/index.html"
})
],
在你webpack.config.js
这里推荐https://webpack.js.org/guides/caching/
这样,主机将始终尝试获取 remoteEntry.js
(没有哈希的固定 url)并让浏览器获取新块 url如果更改哈希值。