---
sidebar_position: 2
title: 体验微前端
---
# 体验微前端

通过本章你可以了解到：

- 如何创建微前端项目的主应用、子应用。
- 微前端项目开发的基本流程。

## 创建应用
目前支持两种路由模式
- 自控式路由
- 约定式路由

首先明确主应用的路由模式 [创建约定式路由主应用](#创建约定式路由主应用) 或 [创建自控式路由主应用](#创建自控式路由主应用)

在本次体验中我们会为主应用创建两个子应用 Table 和 Dashboard (Table 为约定式路由，Dashboard 为自控式路由)

### 创建约定式路由主应用

通过命令行工具初始化项目：

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

import DefaultMWAGenerate from "@site-docs/components/default-mwa-generate";

<DefaultMWAGenerate />

完成项目创建后我们可以通过 `pnpm run new` 来开启 `微前端` 功能：

```bash
? 请选择你想要的操作 启用可选功能
? 请选择功能名称 启用「微前端」模式
```

接下来，让我们注册微前端插件并添加开启微前端主应用，并增加子应用列表：

import EnableMicroFrontend from "@site-docs/components/enable-micro-frontend";

<EnableMicroFrontend />

然后我们在 routes 文件夹下新建两个目录
- table (用于加载约定式路由子应用)
- dashboard (用于加载自控式路由子应用)

在这两个目录下我们需要新建一个 `$.tsx` 文件作为主应用约定式路由的入口($ 代表模糊匹配，即 `/table` 和 `/table/test` 都会匹配到这个 `$.tsx` 作为该路由的入口文件，这会保证在微前端场景下正确加载子应用路由)

#### 加载约定式路由子应用
```js title="src/routes/table/$.tsx"
import { useModuleApps } from '@modern-js/plugin-garfish/runtime';

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

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

export default Index;
```

#### 加载自控式路由子应用
```js title="src/routes/dashboard/$.tsx"
import { useModuleApps } from '@modern-js/plugin-garfish/runtime';

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

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

export default Index;
```
#### 路由跳转
此时主应用配置已经完成，通过路由即可加载子应用，修改主应用的 `layout.tsx` 来跳转路由
```js title="src/route/layout.tsx"
import { Outlet, Link } from '@modern-js/runtime/router';

const Layout = () => (
  <div>
    <div><Link to={'/table'}>加载约定式路由子应用</Link></div>
    <div><Link to={'/dashboard'}>加载自控式路由子应用</Link></div>
    <div><Link to={'/'}>卸载子应用</Link></div>
    <Outlet />
  </div>
);

export default Layout;
```

### 创建自控式路由主应用

通过命令行工具初始化项目：

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

<DefaultMWAGenerate />

完成项目创建后我们可以通过 `pnpm run new` 来开启 `微前端` 功能：

```bash
? 请选择你想要的操作 启用可选功能
? 请选择功能名称 启用「微前端」模式
```

接下来，让我们注册微前端插件并添加开启微前端主应用，并增加子应用列表：

<EnableMicroFrontend />

由于是自控式路由，我们删除掉 `routes` 文件夹并在 `src` 目录下增加 `App.tsx` 文件，此处如果使用的 `非 MApp` 组件，需要通过 `React Router v6` 的 `createBrowserRouter` API 来创建路由
#### 加载子应用
import CustomRouterMicroFrontend from "@site-docs/components/custom-router-micro-frontend";

<CustomRouterMicroFrontend />

### 创建约定式路由子应用

通过命令行工具初始化项目：

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

按照如下选择，生成项目：

<DefaultMWAGenerate/>

我们执行 `pnpm run new` 来开启 `微前端` 功能：

```bash
? 请选择你想要的操作 启用可选功能
? 请选择功能名称 启用「微前端」模式
```

接下来，让我们注册微前端插件并修改 `modern.config.ts`，添加微前端子应用的配置 `deploy.microFrontend`：

```js title="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()],
});
```

添加 `src/routes/page.tsx` 代码
```js title="src/routes/page.tsx"
const Index = () => {
  return (
    <div className="container-box">subApp: 约定式路由的子应用的根路由</div>
  )
}

export default Index;
```

### 创建自控式路由子应用

通过命令行工具初始化项目：

```bash
mkdir dashboard && cd dashboard
npx @modern-js/create@latest
```

按照如下选择，生成项目：

<DefaultMWAGenerate/>

我们执行 `pnpm run new` 来开启 `微前端`：

```bash
? 请选择你想要的操作 启用可选功能
? 请选择功能名称 启用「微前端」模式
```

接下来，让我们注册微前端插件并修改 `modern.config.ts`，添加微前端子应用的配置 `deploy.microFrontend`：

```js title="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()],
});
```
自控式路由需要删除掉 `routes` 文件夹并在 `src` 目录下新建 `App.tsx`

并在 `src/App.tsx` 添加代码，注意需要从 `props` 中解析并传递 `basename` 给 `BrowserRouter`

```js title="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>自控式路由子应用根路由</div>} />
        <Route path={'path'} element={<div>自控式路由子应用子路由</div>} />
      </Routes>
    </BrowserRouter>
  );
};
```

## 调试
按顺序在目录执行 `pnpm run dev` 命令启动应用：
- masterApp 主应用 `http://localhost:8080`
- table 子应用(约定式路由) `http://localhost:8081`
- dashboard 子应用(自控式路由) `http://localhost:8082`

访问主应用地址 `http://localhost:8080`

在完成了微前端整体开发流程的体验后，你可以进一步了解如何 [开发主应用](/guides/topic-detail/micro-frontend/c03-main-app)

## Modern.js 微前端和直接使用 Garfish 的区别

使用纯 `Garfish` API 开发微前端应用时
- 主应用：
  - 安装 Garfish 依赖并使用 `Garfish.run` 注册子应用 [参考](https://www.garfishjs.org/api/run)
  - 提供一个常驻的 `DOM` 节点供子应用挂载 [参考](https://www.garfishjs.org/api/registerApp#domgetter)
- 子应用：
  - 导出 `provider` [参考](https://www.garfishjs.org/guide/start#2%E5%AF%BC%E5%87%BA-provider-%E5%87%BD%E6%95%B0)
  - 设置应用的 `basename` [参考](https://www.garfishjs.org/guide/start#3-%E8%AE%BE%E7%BD%AE%E5%BA%94%E7%94%A8%E8%B7%AF%E7%94%B1-basename)

区别于直接使用 `Garfish` 运行时 API 开发微前端项目，`Modern.js` 的微前端方案更加开箱即用。
使用 `pnpm new` 启用微前端模式后会自动在 `Modern.js` 应用中集成 `Garfish` 插件，在 `Garfish`
插件的加持下，你只需要
- 主应用：
  - 配置 `runtime.masterApp.apps` 参数注册子应用
  - 使用 `useModuleApps` API 获取子应用实例并在组件中完成渲染
- 子应用：
  - 配置 `deploy.microFrontend`

所以插件中为你做了如下事情
  - 帮助你通过 `Garfish` 运行时 API 自动注册子应用（主应用）
  - `useModulesApps` 函数的返回值提供了一个常驻的 `DOM` 节点供子应用挂载（主应用）
  - 帮助你正确导出了 `provider`（子应用）
  - 帮助你正确设置了 `basename` 给 `Modern.js` 运行时提供 `Router` 实例，如果是`自控式路由`或`手动引入的 react-router-dom` 那么需要从 `App.tsx` 的 `props` 中获取 `basename` 手动传递给引入的 `Router 实例`（子应用）

## 常见问题

自查手册: https://www.garfishjs.org/issues/
