Skip to content

Micro Front-end react apps created by Create-React-App, Connect multiple react apps to single parent app.

License

Notifications You must be signed in to change notification settings

miteshtagadiya/microfrontend-react

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

32 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

microfrontend-react

Micro Front-end react apps created by Create-React-App

Micro-frontend architecture is a design approach in which a front-end app is decomposed into individual, semi-independent “microapps” working loosely together.

Th applications are loaded into a micro-frontend container. The container runs it as of the micro frontend is its own component and provides seamless workflow to users.

This is the workflow of how micro front-ends work:

  • Launch container app.
  • Launch sub-app1 and sub-app2 applications on specific ports.
  • Based on the URL, the Container will route to one of the micro front-ends.
  • The selected micro front-end goes to the specific port to fetch the application’s asset-manifest.json. From this JSON file, the included main.js is put on a script tag and loaded.
  • A manifest file contains a mapping of all asset filenames.
  • Container app passes the containerId and history for its micro front-ends to be rendered.

Start creating micro front-end apps:

  • Install "react-app-rewired" — This allows customizing the app without ejecting app.
npm i --save react-app-rewired
  • Modify package.json to set port and use "react-app-rewired" in sub-apps.
 "scripts": {
   "start": "PORT=4001 react-app-rewired start",
   "build": "react-app-rewired build",
   "test": "react-app-rewired test",
   "eject": "react-app-rewired eject"
 },
  • Add config-overrides.js to disable code splitting. By default, code splitting is enabled. An application is split into several chunks that can be loaded onto the page independently. You can see http://localhost:4001/asset-manifest.json before adding react-app-rewired. It clearly shows the app has been chunked.
//config-overrides.js
module.exports = {
  webpack: (config, env) => {
    config.optimization.runtimeChunk = false;
    config.optimization.splitChunks = {
      cacheGroups: {
        default: false,
      },
    };
    return config;
  },
};
  • Make changes in src/index.js to define render and unmount functions.
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";

// render micro frontend function
window.rendersubapp1 = (containerId, history) => {
  ReactDOM.render(
    <App history={history} />,
    document.getElementById(containerId)
  );
  serviceWorker.unregister();
};

// unmount micro frontend function
window.unmountsubapp1 = containerId => {
  ReactDOM.unmountComponentAtNode(document.getElementById(containerId));
};

// Mount to root if it is not a micro frontend
if (!document.getElementById("subapp1-container")) {
  ReactDOM.render(<App />, document.getElementById("root"));
}

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
  • If an app is running independent, it will be rendered to root element. If it is a micro front-end, it will be rendered to containerId by window.rendersubapp1.
  • Use src/setupProxy.js to set up CORS rule.
module.exports = app => {
  app.use((req, res, next) => {
    res.header('Access-Control-Allow-Origin', '*');
    next();
  });
};
  • Configure Your .env File to Set Up a Host for Each Micro-Frontend Application in Container app.
REACT_APP_SUBAPP1_HOST=http://localhost:4001
REACT_APP_SUBAPP2_HOST=http://localhost:4002
  • Add Microfront.js file in sec directory, It picks up a manifest file from a running application and launches the application through a script and link tag.
import React from 'react';

class MicroFrontend extends React.Component {
  componentDidMount() {
    const { name, host, document } = this.props;
    const scriptId = `micro-frontend-script-${name}`;

    if (document.getElementById(scriptId)) {
      this.renderMicroFrontend();
      return;
    }

    fetch(`${host}/asset-manifest.json`)
      .then(res => res.json())
      .then(manifest => {
        manifest["entrypoints"].map((entry => {
          if (typeof manifest["files"][entry] !== "undefined" && manifest["files"][entry] !== "undefined") {
            if (entry.endsWith('.css')) {
              const link = document.createElement('link');
              link.id = scriptId;
              link.href = `${process.env.NODE_ENV === "production" ? host.slice(0, host.lastIndexOf('/')) : host}${manifest["files"][entry]}`;
              link.onload = this.renderMicroFrontend;
              link.rel = "stylesheet"
              document.head.appendChild(link);
            }
            const script = document.createElement('script');
            script.id = scriptId;
            script.crossOrigin = '';
            script.src = `${process.env.NODE_ENV === "production" ? host.slice(0, host.lastIndexOf('/')) : host}${manifest["files"][entry]}`;
            script.onload = this.renderMicroFrontend;
            document.head.appendChild(script);
          }
        })
        )
        const script = document.createElement('script');
        script.id = scriptId;
        script.crossOrigin = '';
        script.src = `${process.env.NODE_ENV === "production" ? host.slice(0, host.lastIndexOf('/')) : host}${manifest["files"]["main.js"]}`;
        script.onload = this.renderMicroFrontend;
        document.head.appendChild(script);
        const link = document.createElement('link');
        link.id = scriptId;
        link.href = `${process.env.NODE_ENV === "production" ? host.slice(0, host.lastIndexOf('/')) : host}${manifest["files"]["main.css"]}`;
        link.onload = this.renderMicroFrontend;
        link.rel = "stylesheet"
        document.head.appendChild(link);
      });
  }

  componentWillUnmount() {
    const { name, window } = this.props;

    window[`unmount${name}`] && window[`unmount${name}`](`${name}-container`);
  }

  renderMicroFrontend = () => {
    const { name, window, history } = this.props;

    window[`render${name}`] && window[`render${name}`](`${name}-container`, history);
  };

  render() {
    return <main id={`${this.props.name}-container`} />;
  }
}

MicroFrontend.defaultProps = {
  document,
  window,
};

export default MicroFrontend;
  • In Container App Create a Micro-Frontend Component for each micro-frontend application and Use a route to invoke it.
import React from "react";
import { NavLink, BrowserRouter, Route, Switch } from "react-router-dom";
import MicroFrontend from "./MicroFrontend";

const {
  REACT_APP_SUBAPP1_HOST: subapp1,
  REACT_APP_SUBAPP2_HOST: subapp2
} = process.env;

const SubApp2 = ({ history }) => (
  <MicroFrontend history={history} host={subapp2} name="subapp2" />
);

const SubApp1 = ({ history }) => (
  <MicroFrontend history={history} host={subapp1} name="subapp1" />
);

const Home = () => (
  <>
    <p>Rendered by Container</p>
  </>
);

const App = props => {
  return (
    <BrowserRouter>
      <div style={{ padding: 25, display: "flex" }}>
        <div style={{ padding: "0px 15px" }}>
          <NavLink
            style={{
              textDecoration: "none",
              fontWeight: "bold",
              color: "#282c34",
              fontSize: 20
            }}
            to="/home"
          >
            Home
          </NavLink>
        </div>
        <div style={{ padding: "0px 15px" }}>
          <NavLink
            style={{
              textDecoration: "none",
              fontWeight: "bold",
              color: "#282c34",
              fontSize: 20
            }}
            to="/subapp1"
          >
            SubApp1
          </NavLink>
        </div>
        <div style={{ padding: "0px 15px" }}>
          <NavLink
            style={{
              textDecoration: "none",
              fontWeight: "bold",
              color: "#282c34",
              fontSize: 20
            }}
            to="/subapp2"
          >
            SubApp2
          </NavLink>
        </div>
      </div>

      <Switch>
        <Route path="/home" component={Home} />
        <Route path="/subapp1" render={() => <SubApp1 />} />
        <Route path="/subapp2" render={() => <SubApp2 />} />
      </Switch>
    </BrowserRouter>
  );
};

export default App;

Run miro front-end Apps:

Container App: http://localhost:3000
SubApp1: http://localhost:4001
SubApp2: http://localhost:4002
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy