Webpack 4 – Import a Prebuilt Webpack Bundle at Runtime

Feature request

Hi currently In our projects, we plan to do Front End Micro, divide an App to multiple repositories.

  • Repo 1: App1

  • Repo 2: App2

  • Repo 3: App3

  • Repo 4: A Shell

  • When user navigate to /app1 we will get the script of App1 storing in CDN for example: http://cdn.company/app1,bundle.js. It looks like a Lazy Load Pattern

  • Can we support the import(‘http://cdn.company/app1.bundle.js‘) at runtime ?

What is the expected behavior?

  • It should map the module export from prebuilt bundle at runtime
import('http://cdn.company/app1.bundle.js').then(module => {
   this.setState({
       loadedComponent: module.default
   })
});

What is motivation or use case for adding/changing the behavior?

  • Front End Micro Architecture

How should this be implemented in your opinion?

  • Make it simple like
import('http://cdn.company/app1,bundle.js').then(module => {
   this.setState({
       loadedComponent: module.default
   })
});

Are you willing to work on this yourself?
yes but don’t know how to do it 😄

Author: Fantashit

9 thoughts on “Webpack 4 – Import a Prebuilt Webpack Bundle at Runtime

  1. I’m personally against this feature because it is an anti-pattern to static analysis. importing an already bundled file causes a significant overhead and duplication that could be statically analyzed and code-split if the codebase is available at build time.

    There have been others that have asked for this but I’m not sure I would endorse our team working on it. “Microservices” for the front-end (in my opinion) is a poor architecture because you remove the capability for your build-time analysis to optimize all the code that you will be shipping down the pipe.

    Instead of what you have described above, I would imagine instead for minimal code waste and optimal reuse:

    App: (your actual app)
    Lazy Route/Component1
    Lazy Route/Component2
    Lazy Route/Component3

    and this would reside in a single MonoRepository leveraging yarn workspaces, etc.

  2. @lovedota this is definitively possible with webpack if you use the tried and true AMD module specification as the output target in your webpack bundles. You will also need to treat each app as an external for each other app. For example, in the webpack configuration for each of your micro-apps:

    module.exports = {
      output: {
        library: "app1", // for subsequent apps, change them to a different name
        libraryTarget: "amd"
      },
      ...
      externals: [
        /^app2$/, // include all other apps that this app may also depend upon
        /^app3$/
      ]
    }

    Given that configuration, when you generate a bundle, you’ll get an AMD module that looks like:

    define('app1', ['app2', 'app3'], function(app2, app3) {
      ...
    });

    Then use a module loader, something like SystemJS or RequireJS to load each application. You’ll need to tell the module loader how app1, app2, and app3 actually map to a URL. Configuring that mapping with SystemJS would look something like:

    SystemJS.config({
      map: {
        app1: 'https://your-sweet-cdn.com/app1.js',
        app2: 'https://your-sweet-cdn.com/app2.js',
        app3: 'https://your-sweet-cdn.com/app3.js'
      }
    });

    Then you can simply load your initial app by doing SystemJS.import('app1');. And then if app1 subsequently loads app2 or app3, SystemJS will properly load those dependencies and make them available.

    While this is a lot of work to get up and running, we have built a variety of tools that make micro-services on the front-end a more enjoyable experience. Checkout the SystemJS plugin, sofe, which makes it easier to map micro-service names to fully qualified URLs. Another project, single-spa, makes it easier to have multiple SPAs dynamically load, unload, and be active on the same page at the same time.

  3. For now, the import() syntax which webpack is supporting is not being fully supported. That’s not documented.

    It would be helpful to document this, as “webpack import() === browser standard import()“, which could be very surprising to some people.

  4. I have a use case for plugin-architecture with angular and webpack where each plugin is served from external urls based on some rest api response i.e. Actual application developer don’t know about the list of plugins but they will be loaded based on rest api response and these are dynamic. I tried to use System Js 3.x with webpack but webpack overrides the import() method and didn’t worked for me. As I don’t know the actual cdn path for plugins so I can’t configure those into externals. So Actually there are 2 problems:

    • Loading of external dynamic modules served from a cdn path whose path is just available at the runtime of the application
    • How to manage dependencies of these run time modules if we are using like systemjs

    I came around a solution but don’t know if that is recommended solution. I use SystemJs 0.20 as webpack don’t overrides the import method of that and configure all the dependencies with SystemJS. Is there any alternate solution where I can use the existing webpack dependency graph instead of SystemJS. Any help would be appreciated 🙂 .

  5. I also have the same issue. I’d like to load multi project basic module. And do it dynamically. I’ve found one decision.

    import SystemJs from 'systemjs/dist/system';
    
    export default async function (cdnUrl) {
      try {
        const data = await SystemJs.import(cdnUrl);
        if (data) {
            ....
            return data;
          } else {
            throw new Error('Undefined data in locale');
          }
      } catch (e) {
        throw e;
      }
    }
    

    With some Webpack hack

    ...
    {
            test: /systemjs/,
            use: [
              {
                loader: 'exports-loader',
                options: {
                  'self.System': true
                }
              },
    }
    ...
    

    But this code is quite unstable and hard linked with systemjs version. Also it needs to be build before.
    Moreover systemjs with this modification still acts in global 🙁

  6. I have built a basic github repo with plugin architecture using only webpack and would like some feedback if possible. It should only take 10 min or so to read the code. https://github.com/psimoneau22/microfrontend-plugin?files=1

    The the code is largely motivated by the write up of this article, which is a great resource: https://www.martinfowler.com/articles/micro-frontends.html

    I will be looking more into Systemjs also and possibly adding web-components as the interface for communicating between parent app and child plugins

Comments are closed.