wujie (无界) Micro-frontend Framework

repository·master·Indexed 26 days ago

https://github.com/tencent/wujie

A micro-frontend framework that combines Web Components and iframes to provide high-performance, natively isolated sub-applications. It features CSS isolation via Web Components and JS isolation via iframes, supporting keep-alive persistence, nested applications, and a plugin system. wujie provides official integration packages for Vue 2, Vue 3, and React, as well as a decentralized EventBus for communication between main and sub-applications.

Tokens
29.4K
Snippets
47
Records
159
Agent score
89%

What's inside wujie

  1. Overview of Wujie Micro-frontend Framework

    master

    Wujie (无界) is a high-performance micro-frontend framework designed to solve common integration challenges such as adaptation costs, style isolation, runtime performance, white-screen issues, sub-app communication, sub-app lifecycle management (keep-alive), multi-app activation, Vite support, and application sharing.

    Core Architecture: It utilizes a combination of WebComponent containers and iframe sandboxing to achieve native-level physical isolation and performance without relying on heavy with statements.

    Key Features:

    • Extreme Speed: Supports preloading and pre-execution to eliminate white screens and ensure smooth transitions.
    • Powerful Capabilities: Supports sub-app keep-alive, embedding, decentralized communication, and multi-app activation.
    • Simplicity: Encapsulated framework that maintains a consistent experience with standard component usage.
    • Native Isolation: Uses WebComponents and iframes for robust isolation.
    • Native Performance: Avoids with statements to keep runtime performance close to native code.
    • Out-of-the-box: Requires no special adaptation from either the main application or the sub-applications.
  2. Overview of wujie (无界) Micro-frontend Framework

    master

    wujie (无界) is a micro-frontend framework that utilizes a combination of Web Components and iframes to achieve low-cost integration, high speed, and native isolation.

    Key features include:

    • Native Isolation: CSS is isolated via Web Components, and JS runs in an iframe to provide a dedicated window, document, history, and location environment.
    • Performance: Fast sub-application loading and execution.
    • Advanced Capabilities: Supports sub-application lifecycle hooks, sub-application persistence (keep-alive), nested applications, multi-application activation, application sharing, decentralized communication, a plugin system, and Vite support.
  3. Understand Wujie's iframe and ShadowRoot isolation mechanism

    master

    Wujie achieves micro-frontend isolation by running sub-applications in a same-origin iframe and using ShadowRoot to contain DOM side effects.

    Key isolation features include:

    • Pure iframe implementation: The iframe is instantiated and its loading is immediately interrupted (iframeWindow.stop()) before it can load the main application's HTML/JS, preventing pollution.
    • Data Hijacking: Sub-application window and location objects are proxied. When using type="module" scripts, you must access the modified location via $wujie.location.
    • Side Effect Handling: The framework automatically patches iframe events (like resize), DOM effects (like MutationObserver), and synchronizes sub-app routing to the main application.
    • ShadowRoot Protection: Side effects within ShadowRoot (specifically head and body manipulations) are intercepted. link/style tags are collected for style reconstruction, script tags are moved to the iframe for execution, and iframe tags are redirected to the sub-app's window.
  4. Key Advantages of Wujie

    master

    Wujie provides several developer-centric benefits:

    • Component-based Usage: Use micro-apps as components. No need for manual registration or complex router adaptations; they load and unload with the component.
    • Application-level Keep-alive: By enabling alive mode, sub-applications can preserve their internal state when switching between apps.
    • Zero-cost Cleanup: Because sub-apps run in an isolated iframe, switching apps requires no manual cleanup of global variables or event listeners.
    • Low Integration Cost: Sub-applications can be integrated "out of the box" with minimal changes to styles, routing, or hot-reloading logic.
  5. Understand the Wujie Micro-frontend Architecture

    master

    Wujie (无界) is a micro-frontend framework designed to run multiple web applications within a single host application. Unlike traditional solutions like single-spa or iframe alone, Wujie combines iframe and WebComponents to achieve high-performance isolation and ease of use.

    Core Mechanisms

    • JS Sandbox via iframe: Sub-applications run their JavaScript inside a same-origin iframe. This provides a native window sandbox with its own history and location, preventing global object pollution without the performance overhead of Proxy-based sandboxes.
    • CSS Sandbox via WebComponents: Wujie uses a custom wujie WebComponent to render the sub-application's DOM. By proxying iframe document queries (like querySelector, getElementById, etc.) to the WebComponent, it achieves physical CSS isolation and solves common issues like modal/popup positioning.
    • Route Synchronization: Wujie hijacks history.pushState and history.replaceState within the iframe to sync the sub-application's URL with the host application's query parameters. This ensures browser back/forward buttons work seamlessly.
    • Communication:
      • props injection: Sub-apps access host data via $wujie.props.
      • window.parent: Since the iframe is same-origin, sub-apps can communicate directly with the host via window.parent.
      • EventBus: A decentralized communication mechanism provided via an EventBus instance injected into all applications.
  6. Navigate between micro-apps in History mode

    master

    When the main application uses history mode, you can navigate from Micro-app A to Micro-app B by passing a jump function via props from the main application to the micro-app.

    1. Basic Navigation (to Micro-app B's default route)

    Main Application Setup (Vue example): Pass a jump function to the micro-app via the :props attribute.

    <template>
      <!-- Micro-app A -->
      <wujie-vue name="A" url="//hostA.com" :props="{jump}"></wujie-vue>
    </template>
    
    <script>
    export default {
      methods: {
        jump(location) {
          this.$router.push(location);
        }
      }
    }
    </script>

    Micro-app A Implementation: Call the jump function provided in window.$wujie.props.

    function handleJump() {
      window.$wujie?.props.jump({ path: "/pathB" });
    }

    2. Navigate to a specific route in Micro-app B

    To navigate to a specific route (e.g., /test) within Micro-app B, Micro-app B must have route synchronization enabled. Use the query property to pass the target path.

    Micro-app A Implementation:

    function handleJump() {
      window.$wujie?.props.jump({ path: "/pathB", query: { B: "/test" } });
    }

    Note: This method only works if Micro-app B has not been activated/instantiated yet. Once activated, it will read the path from the URL query parameters.

    <template>
      <!-- 子应用 A -->
      <wujie-vue name="A" url="//hostA.com" :props="{jump}" ></WujieVue>
    </template>
    
    <script>
    export default {
      methods: {
        jump(location) {
          this.$router.push(location);
        }
    }
    </script>
  7. Fix relative image paths in sub-apps using v-html or innerHTML

    master

    If relative image paths are not being converted to absolute paths (common when using v-html, innerHTML, or dynamic style attributes), you must manually set the public path.

    Solution: At the very top of your sub-app's entry file (e.g., main.js), import a config file that performs the following assignment:

    // In your config file imported at the top of main entry
    if (window.__POWERED_BY_WUJIE__) {
      // eslint-disable-next-line
      window.__webpack_public_path__ = window.__WUJIE_PUBLIC_PATH__;
    }
  8. Preload sub-applications with preloadApp

    master
    Use preloadApp to fetch a sub-application's static resources from the network into memory during idle time using requestIdleCallback. This significantly reduces the initial load time of the sub-application when it is eventually opened.
  9. Handle location correctly using $wujie.location

    master

    Because a sub-application's location.host normally returns the main application's host, Wujie provides a corrected location object mounted on $wujie.location.

    Important Migration/Compatibility Note: In certain scenarios, window.location.host will not be correctly proxied and will return the main application's host instead of the sub-application's host. You must update your code to use $wujie.location.host instead of window.location.host in the following cases:

    1. Vite-based frameworks: When using Vite, script tags use type="module", which prevents Wujie from using closure-based hijacking to proxy location.
    2. Degraded Mode: When the sub-application falls back to a non-shadow DOM mode (degraded mode), the proxy mechanism for location may not function correctly.

    If you are using a non-Vite framework and not in degraded mode, window.location is typically proxied automatically and no changes are required.

  10. Initialize a new Wujie project with create-wujie

    master

    Use create-wujie to quickly bootstrap example projects for testing and development. It allows you to select one or multiple sub-applications and choose routing modes (hash or history) for both the main and sub-applications.

    Prerequisites:

    • Node.js: Version must be less than 18.0.0.
    • Package Manager: The project templates use pnpm and turborepo for management.
  11. Share dependencies between main and sub-applications

    master

    To prevent redundant bundling and memory waste when multiple applications use the same dependency (e.g., lodash), you can share the dependency via the window object using Wujie's plugin system.

    Note: This relies on the main application and sub-application running in the same-origin iframe sandbox. Third-party packages with side effects (like some component libraries) may not be suitable for this method.

    1. Configure the Main Application

    Expose the dependency on the main application's window object:

    // index.js
    import lodash from "lodash";
    
    // Attach the shared package to the main application's global window
    window.lodash = lodash;

    2. Configure the Sub-application (Webpack)

    Set the dependency as an externals in your Webpack configuration so it is not bundled into the sub-application:

    module.exports = {
      externals: {
        "lodash": {
          root: "lodash",
          commonjs: "lodash",
          commonjs2: "lodash",
          amd: "lodash",
        },
      },
    };

    3. Inject the dependency via Wujie Plugin

    When loading the sub-application, use the jsBeforeLoaders plugin to map the main application's window property to the sub-application's window:

    <WujieVue
      name="A"
      url="xxxxx"
      :plugins="[{ jsBeforeLoaders: [{ content: 'window.lodash = window.parent.lodash' }] }]"
    ></WujieVue>