WP5 Module Federation: remoteEntry.js caching

Viewed 8140

With Webpack 5 module federation if remote entry is modified, you don't need to redeploy main module/application and the newest version of the module will be loaded when it's requested by the browser.

I'm wondering: since the remote URL remains the same (e.g. http://localhost:8081/remoteEntry.js), the browser probably will cache the file and cached version loaded every time you load the main module. On the other hand, if you add cache busting for remote entries, you will not have caching.

Let's assume that there is an application with micro-frontend architecture using Webpack 5 Module federation. There is a remote micro frontend with a config like:

output: {
  publicPath: "http://localhost:8081/",
},
plugins: [
  new ModuleFederationPlugin({
    name: "app1",
    filename: "remoteEntry.js",
    exposes: {
      "./Component1": "./src/Component1",
      "./someModule1": "./src/someModule1",
    },
  })
]

and then main module config:

output: {
  publicPath: "http://localhost:8080/",
},
plugins: [
  new ModuleFederationPlugin({
    name: "mainApp",
    filename: "remoteEntry.js",
    remotes: {
      app1: "app1@http://localhost:8081/remoteEntry.js"
    }
  })
]

Both modules are deployed on production.

Then I change Component1 from app1 and deploy app1 module.

How to deal with remote modules caching?

UPDATE:

It looks like in my case the browser uses heuristic caching (https://www.rfc-editor.org/rfc/rfc7234#section-4.2.2) for remoteEntry.js since the server doesn't provide explicit expiration times.

Thus, when remoteEntry.js updates, the main application still loads this file from the cache that could be cached for weeks. For chunks, it's not a problem since webpack could be configured to include hash in the file names.

For remoteEntry.js I see 2 options:

  • cache-busting
  • explicitly specify cache control

Do you think it's a way to go?

5 Answers

Cache busting implies to re-build (or, at least, post-process) the main app bundle, and that is one of the problem module federation tries to avoid.

So, considering remoteEntry.js is usually a small file, the best solution is to apply a specific cache control for this file so it never gets cached.

With nginx, it can be done that way:

location ~ .*remoteEntry.js$ {
    expires -1;
    add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
}

In my opinion, you should never cache remoteEntry.js since that's where the chunks mapping are. (Mapping here I mean between component and their chunk url). You always want to make sure you are displaying the latest remote chunk. If the mapping changed (usually just means the remote component updated), the browser should fetch the new chunk.

That said, you should cache busting the chunks using

    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"
        })
    ],

in your webpack.config.js recommended here https://webpack.js.org/guides/caching/

This way, again, the host will always try to fetch the remoteEntry.js (a fixed url that do not have hash) and let the browser fetch the new chunk url if the hash are changed.

I don't think this is a concern since webpack adds cache busting to your application's js files during the build process. Take a look at the HTML code that is returned when you load your app and you will notice that the tags that load your bundle are different after each deployment. remoteEntry.js is the module federation way of allowing your app to be loaded from a remote server, not your actual app code.

Related