Experience Micro Frontend

Through this chapter you can learn:

  • How to create the main application and sub-application of the micro frontend project.
  • Basic process of micro frontend project development.

Create an app

The routing modes of the current project are divided into the following three types:

  • File-based routing (router: true and file-based)
  • Self-Controlled routing (router: true and create BrowserRouter)
  • Other (router: false and independent installation of react-router-dom in the project)

First, clarify the routing mode of the main application create a file-based routing main App or create a self-controlled main App

In this tutorial we will create two sub-applications Table and Dashboard for the main application (Table is reduced routing, Dashboard is self-controlled routing)

File-based Routing Main App

Initialize the project with a command line:

mkdir masterApp && cd masterApp
npx @modern-js/create@latest

After the project is created, we can enable the micro frontend through pnpm run new:

? Please select the operation you want: Enable features
? Please select the feature name: Enable Micro Front-end Feature

Next, let's register the micro frontend plugin and add the main micro frontend app and add the list of sub-apps:

modern.config.ts
import { appTools, defineConfig } from '@modern-js/app-tools';
import { garfishPlugin } from '@modern-js/plugin-garfish';

export default defineConfig({
  runtime: {
    router: true,
    state: true,
    masterApp: true,
  },
  plugins: [appTools(), garfishPlugin()],
});

Then we create two new directories in the routes folder

  • table (for loading conventional routing sub-applications)
  • dashboard (for loading self-controlled routing sub-applications)

In these two directories, we need to create a $.tsx file as the entry of the main application convention route ($represents fuzzy match, that is, /table and /table/test will match this $.tsx as the entry file of the route, which will ensure that the sub-application route is loaded correctly in the micro frontend scenario)

Load file-base routing sub-app

src/routes/table/$.tsx
import { useModuleApps } from '@modern-js/plugin-garfish/runtime';

const Index = () => {
  const { Table } = useModuleApps();

  return (
    <div>
      <Table />
    </div>
  );
};

export default Index;

Load self-controlled routing sub-app

src/routes/dashboard/$.tsx
import { useModuleApps } from '@modern-js/plugin-garfish/runtime';

const Index = () => {
  const { Dashboard } = useModuleApps();

  return (
    <div>
      <Dashboard />
    </div>
  );
};

export default Index;

At this time, the main application configuration has been completed, and the sub-application can be loaded through the route, and the layout.tsx of the main application can be modified to jump to the route:

src/route/layout.tsx
import { Outlet, Link } from '@modern-js/runtime/router';

const Layout = () => (
  <div>
    <div>
      <Link to={'/table'}>Load file-base routing sub-app</Link>
    </div>
    <div>
      <Link to={'/dashboard'}>Load self-controlled routing sub-app</Link>
    </div>
    <div>
      <Link to={'/'}>unmount sub-app</Link>
    </div>
    <Outlet />
  </div>
);

export default Layout;

Self-Controlled Main App

Initialize the project with a command line:

mkdir masterApp && cd masterApp
npx @modern-js/create@latest

After the project is created, we can enable the micro frontend function through pnpm run new:

? Please select the operation you want: Enable features
? Please select the feature name: Enable Micro Front-end Feature

Next, let's register the micro frontend plugin and add the main micro frontend app and add the list of sub-apps:

modern.config.ts
import { appTools, defineConfig } from '@modern-js/app-tools';
import { garfishPlugin } from '@modern-js/plugin-garfish';

export default defineConfig({
  runtime: {
    router: true,
    state: true,
    masterApp: true,
  },
  plugins: [appTools(), garfishPlugin()],
});

Since it is a self-controlled route, we delete the routes/ folder and add the App.tsx file in the src/ directory. If you use a non-MApp component here, you need to use the createBrowserRouter API of '=React Router v6 to create routes.

Load sub-app

src/App.tsx
import { useModuleApps } from '@modern-js/plugin-garfish/runtime';

import { RouterProvider, Route, createBrowserRouter, createRoutesFromElements, BrowserRouter, Link, Outlet } from '@modern-js/runtime/router';

const AppLayout = () => (
  <>
    <div><Link to={'/table'}>Loading conventional routed sub-applications</Link></div>
    <div><Link to={'/dashboard'}>Loading Self-Controlled Routing Sub-Applications</Link></div>
    <div><Link to={'/'}>Uninstall a sub-application</Link></div>
    <Outlet />
  </>
)

export default () => {
  const { apps, MApp, Table, Dashboard } = useModuleApps();

  // If you are not using the MApp component, you need to use createBrowserRouter to create the route.
  const router = createBrowserRouter(
    createRoutesFromElements(
      <Route path="/" element={<AppLayout />}>
        <Route key={'table'} path={'table/*'} element={<Table />} />
        <Route key={'dashboard'} path={'dashboard/*'} element={<Dashboard />} />
      </Route>
    )
  );

  return (
    // Approach 1: Use MApp to automatically load sub-applications based on the configured activeWhen parameter (this project is configured in modern.config.ts)
    // <BrowserRouter>
    //   <MApp />
    // </BrowserRouter>

    // Approach 2: Manually write Route components to load sub-applications, which is convenient for scenarios that require authentication and other pre-requisite operations
    <>
      <RouterProvider router={router} />
    </>
  );
};

Other Main App

Install and use react-router-dom independently in the project. The only difference from self-controlled routing is that after setting router: false, plugin-garfish cannot obtain useLocation, useHref and other hooks to assist in calculating basename and main and child applications. Route synchronization

react-router-dom@6
react-router-dom@5
import { useLocation, useHref } from 'react-router-dom';
const App = () => {
  const basename = useHref('/table');
  const { Table } = useModuleApps();
  return <Table useLocation={useLocation} basename={basename} />;
};

File-based sub-app

Initialize the project with a command line:

mkdir table && cd table
npx @modern-js/create@latest

After create sub-app. We execute pnpm run new to enable the micro frontend function:

? Please select the operation you want: Enable features
? Please select the feature name: Enable Micro Front-end Feature

Next, let's register the micro frontend plugin and modify modern.config.ts to add the configuration of the micro frontend sub-app deploy.microFrontend:

modern.config.ts
import { appTools, defineConfig } from '@modern-js/app-tools';
import { garfishPlugin } from '@modern-js/plugin-garfish';

export default defineConfig({
  dev: {
    port: 8081,
  },
  runtime: {
    router: true,
    state: true,
  },
  deploy: {
    microFrontend: true,
  },
  plugins: [appTools(), garfishPlugin()],
});

add src/routes/page.tsx:

src/routes/page.tsx
const Index = () => {
  return <div className="container-box">subApp</div>;
};

export default Index;

Self-controlled sub-app

Initialize the project with a command line:

mkdir table && cd table
npx @modern-js/create@latest

After create sub-app. We execute pnpm run new to enable the micro frontend function:

? Please select the operation you want: Enable features
? Please select the feature name: Enable Micro Front-end Feature

Next, let's register the micro frontend plugin and modify modern.config.ts to add the configuration of the micro frontend sub-app deploy.microFrontend:

modern.config.ts
import { appTools, defineConfig } from '@modern-js/app-tools';
import { garfishPlugin } from '@modern-js/plugin-garfish';

export default defineConfig({
  dev: {
    port: 8082,
  },
  runtime: {
    router: true,
    state: true,
  },
  deploy: {
    microFrontend: true,
  },
  plugins: [appTools(), garfishPlugin()],
});

Self-controlled routing needs to delete the routes/ folder and create a new App.tsx in the src/ directory.

And add code in src/App.tsx, note that you need to parse from props and pass basename to BrowserRouter.

src/App.tsx
import { BrowserRouter, Route, Routes } from '@modern-js/runtime/router';

export default (props: { basename: string }) => {
  const { basename } = props;

  return (
    <BrowserRouter basename={basename}>
      <Routes>
        <Route index element={<div>Self-controlled route root</div>} />
        <Route path={'path'} element={<div>Self-controlled sub route</div>} />
      </Routes>
    </BrowserRouter>
  );
};

Debug

Start the application by executing the pnpm run dev command in the directory in sequence:

  • masterApp http://localhost:8080
  • table subapplication (conventional routing) http://localhost:8081
  • dashboard subapplication (self-controlled routing) http://localhost:8082

Access the main application address http://localhost:8080

After completing the experience of the overall development process of micro frontend, you can learn more about how to develop the main application.

FAQ

Garfish issue: https://www.garfishjs.org/issues/