The reason for understanding the micro front end is that most of the pages of my company are mobile phone H5, which are scattered and basically unrelated. Each new page opens a secondary domain name, which is difficult to manage. So I studied the micro front end, although I have heard of it a long time ago

This article explores Qiankun, and build a running basic demo, warehouse address

preface

What is a micro front end? Follow an excerpt from the Qiankun documentation

Techniques, strategies and recipes for building a modern web app with multiple teams that can ship features independently. — Micro Frontends

The micro front end is a technical approach and strategy for multiple teams to build modern Web applications by independently publishing their capabilities.

In my understanding, the micro front end can combine multiple sub-applications of different projects that are not closely related into one project, and it has nothing to do with technology stack. React, Vue and jQuery projects can be displayed on the same page at the same time

So what is Qiankun?

Qiankun is a single spa-based micro front-end implementation library designed to make it easier and painless to build production-usable micro front-end architecture systems

As it is a domestic open source project, the documents are also in Chinese, naturally learning Qiankun is also the most friendly

Qiankun uses two ways

  • Any project installationqiankunAfter using
  • Based on theumijstheplugin-qiankun

In the first way, there is no requirement for the main application and sub-application, as long as you install Qiankun and configure it according to the document, you can run through, but you need to configure a little more things

In the second way, the main application needs to be a UMiJS project, and the sub-application, if it is also a UMiJS project, is very simple to configure and has additional functionality, such as sharing data across application React hooks

So explore both approaches

Use Qiankun in normal projects

Ordinary projects do not need to be framework projects, just a JS, an HTML can be

The main application

Main application installation Qiankun

yarn add qiankun
Copy the code

In the main applicationhtmlAdd an ID ofrootdiv

<div id="root"></div>
Copy the code

The main application ofjsI’ll put in the fileqiankunThe configuration of the

import { registerMicroApps } from "qiankun";

// There are two sub-projects in the repository demo. Here is an example of the create-React-app project
registerMicroApps([
  {
    // The child application has a unique name
    name: "app2".// Subapplication entry
    entry: "//localhost:8002".// The child application mounts the element
    container: "#root".// The subapplication matches the path
    activeRule: "/app2",}]); start();// Micro front end -- start
Copy the code

The child application

The child application here uses the create-React-app project

Modify thewebpackconfiguration

Since you are changing the configuration of WebPack, you need to install React-app-Rewired to change the configuration without eject

yarn add react-app-rewired --dev
Copy the code

Modify thepageage.jsonIn thescripts

"scripts": {
-	"start": "react-scripts start",
+	"start": "react-app-rewired start",
Copy the code

Added react-app-rewired profile

Added config-overrides. Js to the react-app-rewired configuration file in the root directory

const { name } = require("./package");

module.exports = {
  webpack: function override(config, env) {
    // Configure as per the Qiankun document
    config.output.library = `${name}-[name]`;
    config.output.libraryTarget = "umd";
    config.output.jsonpFunction = `webpackJsonp_${name}`;
    return config;
  },
  devServer: function (configFunction) {
    return function (proxy, allowedHost) {
      const config = configFunction(proxy, allowedHost);
      // Micro front end projects Neutron projects must support cross-domain
      config.headers = {
        "Access-Control-Allow-Origin": "*"};returnconfig; }; }};Copy the code

Example Change the ID of the mount element

Change the page mount element ID, because the main application is using the root ID

public/index.html

- <div id="root"></div>
+ <div id="root-cra"></div>
Copy the code

Modify the subapplication entry file

src/index.jsx

Add render function

-ReactDOM.render(
- 
      ,
- document.getElementById('root')
-);

+ const render = () => {
+ ReactDOM.render(
      , document.getElementById("root-cra")); / / modify id
+};

Copy the code

Added the Qiankun lifecycle hook

// Run independently without being an Qiankun
// qiankun will inject the __POWERED_BY_QIANKUN__ variable
// If there is no such variable, it means that the page node is not a child application and is directly rendered
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

/** * Bootstrap will only be called once when the microapplication is initialized. The next time the microapplication is re-entered, the mount hook will be called directly. Bootstrap will not be triggered again. * This is usually a place to initialize global variables, such as application-level caches that are not destroyed during the unmount phase. * /
export async function bootstrap() {
  console.log("app2 create-react-app bootstraped");
}
/** * Every time an application enters, it calls the mount method, where we normally trigger the application's render method */
export async function mount(props) {
  console.log("app2 create-react-app mount", props);
  // Call render to the child application
  render();
}
/** * the method is called every time the application is switched out/uninstalled. Usually in this case we will uninstall the application instance of the microapplication */
export async function unmount() {
  console.log("app2 create-react-app unmount");
  ReactDOM.unmountComponentAtNode(document.getElementById("root-cra")!);
}
Copy the code

Access to the main application address localhost: 8080, is the main application, access localhost: 8080 / app2 create – react – app as part of the application, if the application routing can also be accessed directly, such as localhost: 8000 / app2 / page1

Use Qiankun in umijs

In umijs, you only need to add the plugin-Qiankun corresponding to the main application

The main application

Install the plugin – qiankun

yarn add @umijs/plugin-qiankun@next --dev
Copy the code

The new document. Ejs

Create SRC /pages/document.ejs, umi convention if this file exists, it will be the default template


      
<html>
<head>
    <meta charSet="utf-8"/>
    <title>micro frontend</title>
</head>
<body>
<div id="root-subapp"></div>
</body>
</html>
Copy the code

In plugin-Qiankun, children are mounted in root-subapp by default. If they are not mounted in root-subapp, an error will be reported

Modify the configuration.umirc.ts

import { defineConfig } from 'umi';

export default defineConfig({
  qiankun: {
    master: {
      // Register the subapplication information
      apps: [
        {
          name: 'app1'./ / the only id
          entry: '//localhost:8001'.// html entry
          base: '/app1'.// Route prefix of app1. This prefix is used to determine whether to start the application, which is usually consistent with the base of the child application
          history: 'browser'.// By default, the history configuration of the child application is the history configuration of the current main application
          // The child application can use the props parameter to get the value passed in
          props: {},
        },
      ],
      jsSandbox: true.// Whether to enable js sandbox, default is false
      prefetch: true.// Whether to enable prefetch. The default value is true,}}});Copy the code

The child application

The umijs subapplication is very simple and only needs to be modified. Umirc.ts

import { defineConfig } from 'umi';

export default defineConfig({
  base: `/app1`.// The base of the child application, which defaults to the name field in package.json
  qiankun: { slave: {} },
});
Copy the code

Globally shared data

In the ordinary Qiankun and Umijs it is different again

Ordinary Qiankun project

Normal Qiankun can be defined using the initGlobalState method

The main application

import { initGlobalState } from 'qiankun';
// Initialize the state
const actions = initGlobalState(state);
actions.onGlobalStateChange((state, prev) = > {
  // state: indicates the state after the change; Prev Indicates the status before the change
  console.log(state, prev);
});
actions.setGlobalState(state); / / modify state
actions.offGlobalStateChange(); // Remove the current application state listener, which is called by default when the microapplication is umount
Copy the code

The child application

Umijs subapplication hook functions need to be defined in SRC /app.js

export const qiankun = {
    // Obtain the communication method from the life cycle hook function mount. Use the same method as master
    async mount(props) {
      props.onGlobalStateChange((state, prev) = > {
        // state: indicates the state after the change; Prev Indicates the status before the change
        console.log(state, prev);
      });
      props.setGlobalState(state); / / set}}Copy the code

Since methods and events are only available in the hook function, I thought I could register a method like Event Bus at mount time to call the modified function globally

Umijs’ Qiankun project

Plugin-umi provides a convenient React hook for global calls

The main application

Export content in SRC/rootexports.js

let data = ' ';
let eventList = [];

export function getData() {
  return data;
}

export function bindOnChange(fn) {
  if (typeof fn === 'function') {
    eventList.push(fn);
  }
  return function unBind() {
    eventList = eventList.filter(v= >v ! == fn); }; }export function setData(newData) {
  data = newData;
  eventList.forEach(cb= > cb(data));
}
Copy the code

The child application

// ...
const { bindOnChange, setData } = useRootExports();
useEffect((a)= > {
    const unBind = bindOnChange((data) = > {
        console.log('root exports data change:', data);
    });
    return (a)= >unBind(); } []);Copy the code

Note that if the sub-application is running separately or the main application is not based on umijs, this hook will report an error

Delivered directly by configuration

apps: [
    {
        name: 'app1'./ / the only id
        // ...
        // Pass to the child application
        props: {
            username: 'zhangyu'}},].Copy the code

The child application arguments in the lifecycle hook function can get the contents of the props

In Qiankun this configuration can be loaded dynamically; this article only explores fixed configurations


This article complete demo warehouse address

Finally, I wish you all good health and smooth work!