micro-app

Micro-app is a lightweight, efficient and powerful micro-front-end framework

It’s super easy

preface

This article uses Micro-app and React (create-react-app generated projects) to implement the content in this article.

Specifically, it will realize the jump between two projects through the base, and the state in the two pages will not be lost after the jump.

Noun explanation

Pedestal application: Container used to hold sub-applications (business items), communicate with each sub-application, mainly to display the sub-application.

Sub-applications: front-end projects, such as react project, VUE project, ng project, etc

The project structure

-- root # project root -- main # Base app -- React project directory -- One # subapp 1 -- React project directory -- two # subapp 2 -- React project directoryCopy the code

Sub-application code writing

The simple implementation page has an input box and a button to jump to another project.

Write the following code directly in the project entry file

// The first project one/ SRC /index.js

import './public-path'
import React from "react";
import { render } from "react-dom";
import { HashRouter, Switch, Route } from "react-router-dom";

const App = () = > {
    return (
        <HashRouter>
            <Switch>
                <Route path="/Test001">
                    <input type="text" />
                    <button
                        onClick={()= >{ const data = window.microApp? .getData(); data? .pushState("/two/#/Test002"); }} > To the two project</button>
                </Route>
            </Switch>
        </HashRouter>
    );
};
const rootDom = document.getElementById("root");
render(<App />, rootDom);

Copy the code

Ps: [email protected] version is not the same as above, Switch can be changed to Routes.

// The second project two/ SRC /index.js

import './public-path'
import React from "react";
import { render } from "react-dom";
import { HashRouter, Switch, Route } from "react-router-dom";

const App = () = > {
    return (
        <HashRouter>
            <Switch>
                <Route path="/Test002">
                    <input type="text" />
                    <button
                        onClick={()= >{ const data = window.microApp? .getData(); data? .pushState("/one/#/Test001"); }} > To the ONE project</button>
                </Route>
            </Switch>
        </HashRouter>
    );
};
const rootDom = document.getElementById("root");
render(<App />, rootDom);

Copy the code

The two subprojects above are almost identical, except for the text in the buttons and the routing of the page.

Note that the getData function that executes after the button is clicked is not available at this time, it is a method injected by the base.

Run both projects and you’ll see the following page (I ran ports 81 and 82 respectively)

public-path.js

    // __MICRO_APP_ENVIRONMENT__ and __MICRO_APP_PUBLIC_PATH__ are global variables injected by micro-app
    if (window.__MICRO_APP_ENVIRONMENT__) {
         // eslint-disable-next-line 
         __webpack_public_path__ = window.__MICRO_APP_PUBLIC_PATH__
    }
Copy the code

Child application Settings cross domains

Local development requires cross-domain Settings for webPack servers, and online production requires cross-domain Settings for Nginx.

Webpack configuration is cross-domain

Using the create – react – app scaffolding created project, in the config/webpackDevServer config. Js file headers (here is the use of yarn rejest webpack exposed to some file, There are other ways to solve the problem).

Other projects add headers to webpack-dev-server.

headers: {
  'Access-Control-Allow-Origin': '*',
}
Copy the code

Nginx configuration across domains

location / { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization'; if ($request_method = 'OPTIONS') { return 204; }}Copy the code

Base application code

In order to make it easier to read the relationship between each code section, all the code is put into a code file


// Base project main/ SRC /index.js

import React from "react";
import { render } from "react-dom";
import { BrowserRouter, Switch, Route, Link, useHistory } from "react-router-dom"; 

React does not support custom events, so we need to introduce a polyfill.
/ * *@jsxRuntime classic */
/ * *@jsx jsxCustomEvent */
import jsxCustomEvent from "@micro-zoe/micro-app/polyfill/jsx-custom-event";

// entry
import microApp from "@micro-zoe/micro-app";

// Define the data for the two child applications
const apps = [
    { name: "appOne".url: "http://localhost:81/" },
    { name: "appTwo".url: "http://localhost:82/"},];// Preload the child application
microApp.preFetch(apps);
microApp.start();

// Subapplication 1
const Page = () = > { 
   
    // Jumps between sub-applications must be implemented by the base
    // If the location.href = 'XXX' jump is used in the page, keep-alive will not be cached
    const history = useHistory();
    function pushState(path) {
        history.push(path);
    }
    return (
        <>
            <span style={{ fontSize: 15.paddingRight: 6}} >Currently applying 1</span> 
            <micro-app
                // name(Mandatory) : Application namename={apps[0].name} 
                // url(Required) : Indicates the application address, which will be automatically completedhttp://localhost:3000/index.html
                url={apps[0].url}// The page state will not be lost after switching applicationskeep-alive// The data passed into the child applicationdata={{
                    pushState,}} ></micro-app>
        </>
    );
};

// Subapplication 2
const PageTwo = () = > { 
    const history = useHistory();
    function pushState(path) {
        history.push(path);
    }
    return (
        <>
            <span style={{ fontSize: 15.paddingRight: 6}} >Currently in application 2</span> 
            <micro-app
                name={apps[1].name} 
                url={apps[1].url}  
                keep-alive
                data={{ 
                        pushState,}} ></micro-app>
        </>
    );
};

/ / routing
const RouterLay = () = > {
    return (
        <BrowserRouter basename="/main">
            <Switch>
                <Route path="/one">
                    <Page />
                </Route>
                <Route path="/two">
                    <PageTwo />
                </Route>
            </Switch>
        </BrowserRouter>
    );
};

const App = () = > {
    return <RouterLay />;
};

const rootDom = document.getElementById("root");
render(<App />, rootDom);
Copy the code

Briefly analyze a wave of code above

First, the project has two routes /one and /two

The two routes correspond to two function components respectively. The contents of the two function components are almost the same. They call the Micro-App component and then pass a data to the sub-application, providing the sub-application with the ability to jump to another sub-application.

Open base item

Show a wave of code

😑 is not super simple (thanks for the wheels provided by JINGdong) ~

Although it is simple to use, it still takes some time to use in the production environment. The main time is spent in routing processing, direct data sharing between sub-applications… . The child application needs to call the method provided by the base to jump (not so much if you don’t worry about caching pages).

If the system has tabs at the top and switches between subsystems, synchronize the tabs… 🤔