Build a Micro‑Frontend Framework from Scratch with Vue3, React15/16 and TypeScript

This article walks through creating a full‑featured micro‑frontend framework using TypeScript, covering sub‑application registration, router interception, sandbox isolation, CSS scoping, inter‑app communication, global state management, caching, and prefetching, all demonstrated with Vue3 as the host and Vue2, React15, and React16 as child apps.

Alibaba Cloud Developer
Alibaba Cloud Developer
Alibaba Cloud Developer
Build a Micro‑Frontend Framework from Scratch with Vue3, React15/16 and TypeScript

Overview

The guide details how to implement a lightweight micro‑frontend framework from zero, using TypeScript and a Vue3 host application that loads three child applications built with Vue2, React15, and React16. It covers registration, routing, sandboxing, CSS isolation, communication, global store, caching, and prefetching.

Key Features Implemented

Sub‑application registration and resource loading

Runtime isolation (sandbox)

CSS isolation

Application communication (props & custom events)

Global store for shared state

Prefetch and caching for performance

Project Structure

.
├── main
│   └── micro   // framework core
├── vue2   // sub‑app
├── react15
├── react16
└── README.md

Implementation Highlights

1. Sub‑application registration

export interface IAppDTO {
  name: string;
  activeRule: string;
  container: string;
  entry: string;
}
export const subNavList: IAppDTO[] = [
  { name: 'react15', activeRule: '/react15', container: '#micro-container', entry: '//localhost:9002/' },
  { name: 'react16', activeRule: '/react16', container: '#micro-container', entry: '//localhost:9003/' },
  { name: 'vue2',   activeRule: '/vue2',   container: '#micro-container', entry: '//localhost:9004/' }
];

2. Router interception

export const patchRouter = (globalEvent, eventName) => {
  return function () {
    const e = new Event(eventName);
    globalEvent.apply(this, arguments);
    window.dispatchEvent(e);
  };
};
window.history.pushState = patchRouter(window.history.pushState, 'micro_push');
window.history.replaceState = patchRouter(window.history.replaceState, 'micro_replace');
window.addEventListener('micro_push', turnApp);
window.addEventListener('micro_replace', turnApp);
window.onpopstate = function () { turnApp(); };

3. Lifecycle handling

export const registerMicroApps = (appList, lifeCycle) => {
  setList(appList);
  lifeCycle.beforeLoad[0]();
  setTimeout(() => lifeCycle.mounted[0](), 2000);
  setMainLifeCycle(lifeCycle);
};

4. Sandbox isolation

Two sandbox strategies are provided.

Snapshot sandbox

export class SnapShotSandbox {
  constructor() {
    this.proxy = window;
    this.active();
  }
  active() {
    this.snapshot = new Map();
    for (const key in window) {
      this.snapshot[key] = window[key];
    }
  }
  inactive() {
    for (const key in window) {
      if (window[key] !== this.snapshot[key]) {
        window[key] = this.snapshot[key];
      }
    }
  }
}

Proxy sandbox

let defaultValue = {};
export class ProxySandbox {
  constructor() {
    this.active();
  }
  active() {
    this.proxy = new Proxy(window, {
      get(target, key) {
        if (typeof target[key] === 'function') {
          return target[key].bind(target);
        }
        return defaultValue[key] || target[key];
      },
      set(target, key, value) {
        defaultValue[key] = value;
        return true;
      }
    });
  }
  inactive() {
    defaultValue = {};
  }
}

5. CSS isolation

Common approaches include CSS modules, Shadow DOM, and MiniCssExtractPlugin (used in this framework).

6. Communication

Props‑based example passes navStatus and changeNav from the main store to sub‑apps. Custom‑event bus example:

export class Custom {
  on(name, cb) { window.addEventListener(name, e => cb(e.detail)); }
  emit(name, data) { window.dispatchEvent(new CustomEvent(name, { detail: data })); }
}
window.custom = new Custom();

7. Global store

export const createStore = (initData = {}) => {
  let store = initData;
  const observers = [];
  const getStore = () => store;
  const updateStore = newValue => new Promise(res => {
    if (newValue !== store) {
      const oldValue = store;
      store = newValue;
      res(store);
      observers.forEach(fn => fn(newValue, oldValue));
    }
  });
  const subscribeStore = fn => observers.push(fn);
  return { getStore, updateStore, subscribeStore };
};

8. Performance – caching & prefetch

const cache = {};
export const parseHtml = async (entry, name) => {
  if (cache[name]) return cache[name];
  const html = await fetchResource(entry);
  // …resource extraction…
  cache[name] = [dom, script];
  return [dom, allScript];
};
export const prefetch = async () => {
  const list = getList().filter(item => !window.location.pathname.startsWith(item.activeRule));
  await Promise.all(list.map(item => parseHtml(item.entry, item.name)));
};

Running the Demo

Start each sub‑application (e.g., cd react16 && yarn start) and then run the root yarn start script to launch all three apps together.

Micro‑frontend layout
Micro‑frontend layout
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

micro-frontendsandboxVue3prefetchglobal-state
Alibaba Cloud Developer
Written by

Alibaba Cloud Developer

Alibaba's official tech channel, featuring all of its technology innovations.

0 followers
Reader feedback

How this landed with the community

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.