Frontend Development 18 min read

A Brief Discussion on Remote Component Loading Solutions for Low‑Code Platforms

This article examines low‑code development platforms and presents three remote component loading strategies—global‑object, AMD (require.js), and ESModule—detailing their packaging, CDN deployment, loading logic, advantages, drawbacks, and a final recommendation for ESModule‑based loading in modern front‑end development.

政采云技术
政采云技术
政采云技术
A Brief Discussion on Remote Component Loading Solutions for Low‑Code Platforms

Preface

Low‑code development platforms (LCDP) enable rapid application creation with little or no code by using visual drag‑and‑drop components and model‑driven logic. Many companies now build their own low‑code platforms to reduce cost and increase efficiency. This article shares a problem encountered during platform development and proposes solutions.

Problem

Low‑code platforms rely on a rich component library that must be loaded on demand. While tools like Webpack support code splitting, the platform’s core application is separate from its components, requiring remote loading of component code when a user selects a component.

Loading Solutions

Component Code Example (Vue)

Below is a simple Vue component (Component A) that will be used in the remote‑loading examples.

<template>
    <div class="wp">{{text}}</div>
</template>

<script>
import { defineComponent, ref } from 'vue';
import _ from 'lodash';
  
export default defineComponent({
  setup(props) {
    console.log(_.get(props, 'a'));
    return {
      onAdd,
      option,
      size,
      text: 'hello world',
    };
  },
});
</script>

<style>
.wp {
    color: pink;
}
</style>

Solution 1: Put on Global Object

Steps

Package the component as a UMD bundle and configure Webpack externals so that common dependencies are excluded.

Upload the generated JS file to a CDN.

When the component is needed, insert a <script> tag that assigns the component to a global object.

After the script loads, retrieve the component from the global object and register it with the Vue app.

Component Packaging

Add an entry file that attaches the component to window.share :

import Component from './index.vue';

if (!window.share) {
  window.share = {};
}

window.share[Component.name] = Component;

Then use Webpack to output a UMD bundle:

// Component packaging Webpack config
const path = require('path');
const { VueLoaderPlugin } = require('vue-loader');

module.exports = {
  mode: 'production',
  entry: path.resolve(__dirname, './comps/index.js'),
  output: {
    filename: 'index.js',
    path: path.resolve(__dirname, 'dist'),
    library: { type: 'umd' }
  },
  module: {
    rules: [
      { test: /\.vue$/, use: 'vue-loader', exclude: /node_modules/ },
      { test: /\.js$/, loader: 'babel-loader' },
      { test: /\.css$/, use: ['vue-style-loader', 'css-loader'] }
    ]
  },
  plugins: [new VueLoaderPlugin()],
  externals: { vue: 'vue', lodash: 'lodash' }
};

HTML Template

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn/vue.global.js"></script>
    <script src="https://cdn/[email protected]"></script>
</head>
<body>
    <div id="root"></div>
</body>
</html>

Component Loading Logic

const loadComponent = (name) => new Promise((resolve) => {
  const script = document.createElement('script');
  script.src = `http://xxx/${name}.js`;
  script.onload = script.onreadystatechange = function() { resolve(); };
  document.querySelector('head').appendChild(script);
});

const addComp = async (name) => {
  await loadComponent(name);
  // register component, where app is the Vue instance
  app.component(name, window.share[name]);
};

addComp('A');

Drawbacks

All component dependencies must be loaded globally, requiring frequent changes to the HTML template.

The global object grows with each component, hurting load performance and not achieving true on‑demand loading.

Dependency version conflicts arise because only one version can exist on the global object.

Solution 2: AMD (Require.js)

Use Require.js as the AMD loader.

Steps

Package the component as UMD or AMD, keeping common dependencies external.

Upload the bundle to a CDN.

Load and register the component via Require.js when needed.

Component Packaging

No extra entry file is needed; the .vue file can be the entry point. Webpack configuration is similar to Solution 1, but the output library type is set to umd (which also supports AMD).

// Component packaging Webpack config (AMD)
const path = require('path');
const { VueLoaderPlugin } = require('vue-loader');

module.exports = {
  mode: 'production',
  entry: path.resolve(__dirname, './comps/index.vue'),
  output: {
    filename: 'index.js',
    path: path.resolve(__dirname, 'dist'),
    library: { type: 'umd' } // supports AMD
  },
  module: { /* same rules as before */ },
  plugins: [new VueLoaderPlugin()],
  externals: { vue: 'vue', lodash: 'lodash' }
};

HTML Template

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./require.js"></script>
</head>
<body>
    <div id="app"></div>
</body>
</html>

Component Loading Logic

// main.js
requirejs.config({
  baseUrl: 'https://cdn.xxx.com',
  map: { '*': { css: 'require-css' } },
  paths: {
    echarts: '[email protected]',
    vueDemo: 'vue-demo',
    vue: '[email protected]',
    moment: 'https://cdn/[email protected]'
  },
  shim: { 'ant-design-vue': ['css!https://cdn/[email protected]'] }
});

requirejs(['vue', 'vue-demo', 'vue-app'], function (vue, vueDemoModule, VueAppModule) {
  const app = Vue.createApp(VueAppModule.default);
  app.component('vue-demo', vueDemoModule.default);
  app.mount('#app');
});

Drawbacks

The platform code itself must also be compiled to AMD and uploaded, changing the release workflow.

Some third‑party libraries do not provide AMD/UMD builds, requiring custom conversion tools.

Advantages

Component dependencies can have different versions without conflict.

Both components and their dependencies can be truly loaded on demand.

Require.js offers built‑in CSS loading support.

Solution 3: ESModule

Steps

Package the component as an ES module (esm) while keeping common dependencies external.

Upload the generated JS file to a CDN.

Load and register the component with dynamic import() at runtime.

Component Packaging

Configure Webpack to output an ES module and map externals to CDN URLs.

import path from 'path';
import VueLoader from 'vue-loader';
const VueLoaderPlugin = VueLoader.VueLoaderPlugin;
const __dirname = path.resolve();

export default {
  mode: 'development',
  entry: path.resolve(__dirname, './src/vue-demo.vue'),
  output: {
    filename: 'vue-demo.esm.js',
    path: path.resolve(__dirname, 'components'),
    library: { type: 'module' }
  },
  experiments: { outputModule: true },
  module: { /* same loaders as before */ },
  plugins: [new VueLoaderPlugin()],
  externals: {
    vue: 'https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.esm-browser.js',
    lodash: 'https://cdn.jsdelivr.net/npm/[email protected]/lodash.js'
  }
};

After bundling, imports of 'vue' are replaced with the CDN URL.

// input
import Vue from 'vue';

// output
import Vue from 'https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.esm-browser.js';

Component Loading Logic

const list = ref([]);

const addComp = async () => {
  const VueDemo = await import(/* @vite-ignore */ `http://cdn/components/vue-demo.esm.js`);
  window.app.component('vue-demo', VueDemo.default);
  list.value.push({ key: new Date().valueOf(), name: 'vue-demo' });
};

Vite Configuration

Alias the Vue import to the CDN version so that local development also uses the remote module.

// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      'vue': 'https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.esm-browser.js'
    }
  }
});

Drawbacks

Browser compatibility: many features used by Webpack are not yet supported in all browsers.

Converting existing CommonJS packages to ES modules can be cumbersome, and many npm packages still ship only CommonJS code, requiring extra tooling.

Advantages

True on‑demand loading with a clean, modern syntax.

More elegant code structure compared with script‑injection approaches.

About Webpack Module Federation

Based on the author’s experience, Webpack Module Federation is better suited for micro‑frontend scenarios and is not ideal for low‑code platforms, though opinions may vary.

Conclusion

Solution 1 is the simplest to implement but does not achieve real on‑demand loading. Solutions 2 (Require.js) and 3 (ESModule) both provide true on‑demand loading; Require.js has fewer compatibility issues despite its age, while ESModule is the future direction thanks to tools like Vite and Snowpack. The author is currently migrating their internal low‑code platform to ESModule loading.

References

Require.js documentation – https://www.requirejs-cn.cn/

ESModule series – https://mp.weixin.qq.com/s/0AHmP70HnLUZeJWQlRtUKw

Require.js loading CSS – https://blog.csdn.net/lihefei_coder/article/details/81333036

FrontendLow-CodeVueWebpackESMAMDremote component
政采云技术
Written by

政采云技术

ZCY Technology Team (Zero), based in Hangzhou, is a growth-oriented team passionate about technology and craftsmanship. With around 500 members, we are building comprehensive engineering, project management, and talent development systems. We are committed to innovation and creating a cloud service ecosystem for government and enterprise procurement. We look forward to your joining us.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.