Search code examples
vue.jswebpackdynamicvuejs2webpack-module-federation

How to dynamically access a remote component in vue js with module federation


I am trying to build a vue js 2 microfrontend with module federation. I dont want to use static remote imports via the webpack.config.js like this

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'host',
      remotes: {
        app1: 'app1@http://localhost:3001/remoteEntry.js',
      },
    }),
  ],
};

I am looking for a way to dynamically import vue components into my host application. I tried this approach so far, but i only found examples that worked with angular or react.

The goal is to have multiple remote frontends that can automatically register somewhere, maybe in some kind of store. The host application then can access this store and get all of the registered remote applications (name, url, components). The host application then loads the components and should be able to use them. I remote import the component HelloDerp, the loading process is working fine but i dont know how to render it on my host application. I read the vue js doc about dynamic and async imports but i think that only works for local components. What i've got so far in the host application:

<template>
  <div id="app">
    <HelloWorld />
    <HelloDerp />
  </div>
</template>

<script>
import HelloWorld from "./components/HelloWorld.vue";
const HelloDerp = null;


export default {
  name: "App",
  components: {
    HelloWorld,
    HelloDerp,
  },
  mounted() {
    var remoteUrlWithVersion = "http://localhost:9000/remoteEntry.js";
    const element = document.createElement("script");

    element.type = "text/javascript";
    element.async = true;
    element.src = remoteUrlWithVersion;
    element.onload = () => {
      console.log(`Dynamic Script Loaded: ${element.src}`);
      HelloDerp = loadComponent("core", "./HelloDerp");
    };

    document.head.appendChild(element);

    return null;
  },
};

async function loadComponent(scope, module) {
  // Initializes the shared scope. Fills it with known provided modules from this build and all remotes
  await __webpack_init_sharing__("default");
  const container = window[scope]; // or get the container somewhere else
  // Initialize the container, it may provide shared modules
  await container.init(__webpack_share_scopes__.default);
  const factory = await window[scope].get(module);
  const Module = factory();
  return Module;
}
</script>

Solution

  • Sorry i almost forgot about this. Here's my solution.

    Load Modules:

    export default async function loadModules(
      host: string,
      ownModuleName: string,
      wantedNames: string[]
    ): Promise<RemoteComponent[]> {
        ...
        uiApplications.forEach((uiApplication) => {
          const remoteURL = `${uiApplication.protocol}://${uiApplication.host}:${uiApplication.port}/${uiApplication.moduleName}/${uiApplication.fileName}`;
    
          const { componentNames } = uiApplication;
          const { moduleName } = uiApplication;
          const element = document.createElement('script');
          element.type = 'text/javascript';
          element.async = true;
          element.src = remoteURL;
    
          element.onload = () => {
            componentNames?.forEach((componentName) => {
              const component = loadModule(moduleName, `./${componentName}`);
    
              component.then((result) => {
                if (componentName.toLowerCase().endsWith('view')) { 
                // share views
                  components.push(new RemoteComponent(result.default, componentName));
                } else { 
                // share business logic
                  components.push(new RemoteComponent(result, componentName));
                }
               });
            });
          };
          document.head.appendChild(element);
        });
      });
      ...
    }
    
    export default async function loadModule(scope: string, module: string): Promise<any> {
      await __webpack_init_sharing__('default');
      const container = window[scope]; // or get the container somewhere else
      await container.init(__webpack_share_scopes__.default);
      const factory = await window[scope].get(module);
      const Module = factory();
      return Module;
    }
    

    Add Modules to routes

     router.addRoute({
            name: remoteComponent.componentName,
            path: `/${remoteComponent.componentName}`,
            component: remoteComponent.component,
      });