Учебник: Создание микрофронтенда с использованием React, Next.js, Typescript и Федерации модулей


Оглавление

  • Введение
  • Используемые термины
  • Создайте монорепо проекта
  • Создание хоста с помощью Next.js
  • Настройка Typescript в Next.js
  • Создание удаленного проекта с помощью React, Typescript и Webpack 5
  • Настройка федерации модулей
  • Заключение
  • Репозиторий на Github
  • Ссылки

Введение

Наверняка вы, если вы из области технологий, слышали о Micro Frontends, и, возможно, также слышали о Module Federation.

Micro Frontend — это, по сути, распространение концепции микросервисов на Frontend. Федерация модулей — это функция Webpack 5, которая поднимает построение микрофронтендов на новый уровень. Я намерен более концептуально подойти к этим темам в другой статье.

Цель этой статьи — перейти к практике и показать, как с нуля создать простую структуру для проекта Micro Frontend, используя React, Next.js, Typescript и Module Federation.

Поехали!


Используемые термины

Сначала объясним некоторые термины, которые мы будем использовать в этой статье:

HOST: Это центральное приложение (оболочка), которое будет отвечать за загрузку объединенных удаленных компонентов. Здесь мы будем использовать Next.js.

REMOTE: Это приложение, которое будет совместно использовать компоненты с HOST. Он будет построен с помощью React, без использования CRA.

Давайте перейдем к нашему пошаговому описанию:


Создание проекта monorepo

Теперь пришло время открыть терминал и давайте host!

Начнем с создания папки проекта:

mkdir next-react-typescript-mfe

cd next-react-typescript-mfe
Войдите в полноэкранный режим Выход из полноэкранного режима

Давайте начнем наш проект:

yarn init -y -p
git init # opcional caso queira realizar o controle de versão com o Git
Войдите в полноэкранный режим Выход из полноэкранного режима

На данный момент единственные зависимости, которые мы собираемся установить, это Typescript, Concurrently и некоторые опечатки:

yarn add -D typescript @types/react @types/react-dom 
  @types/node concurrently
Войдите в полноэкранный режим Выход из полноэкранного режима

Эти зависимости будут использоваться совместно с проектами, которые мы будем иметь внутри нашего монорепо. Для управления монорепо мы будем использовать Yarn Workspaces.

Мы также можем добавить файл .gitignore со следующим содержимым (необязательно):

.gitignore

node_modules
Войдите в полноэкранный режим Выход из полноэкранного режима

Создание хоста с помощью Next.js

Чтобы создать наш проект HOST, давайте введем следующую команду:

npx create-next-app host
Войдите в полноэкранный режим Выход из полноэкранного режима

В конце процесса у нас будет папка host с готовой установкой Next.js.

Закончив с предыдущим процессом, мы можем добавить проект host в настройки рабочего пространства, внутри package.json в корне проекта:

package.json:

{
  // ...
  "workspaces": ["host"], // Adicionar aqui
  // ...
}
Войдите в полноэкранный режим Выход из полноэкранного режима

Настройка Typescript в Next.js

Настроить Typescript довольно просто, достаточно создать файл tsconfig.json в папке host и внести некоторые небольшие изменения.

touch host/tsconfig.json
Войдите в полноэкранный режим Выход из полноэкранного режима

При следующем запуске проекта будет создан next-env.d.ts и настройки будут заполнены в tsconfig.json.

Для начала мы можем выполнить команду:

yarn workspace host dev
Войдите в полноэкранный режим Выход из полноэкранного режима

Хост-проект настроен, пришло время переименовать наши файлы, чтобы они имели расширение ts или tsx. Для этого вы можете использовать вашу IDE (например, VS Code), файловый менеджер или командную строку:

mv host/pages/_app.js host/pages/_app.tsx
mv host/pages/index.js host/pages/index.tsx
Войдите в полноэкранный режим Выход из полноэкранного режима

Создание удаленного проекта с помощью React, Typescript и Webpack 5

Пора создать наше удаленное приложение. Здесь у нас будет немного больше работы, так как мы не будем использовать create-react-app, чтобы иметь больше контроля над настройками.

Мы начнем с создания папки проекта и запуска проекта:

mkdir remote
cd remote
yarn init -y -p
Войдите в полноэкранный режим Выход из полноэкранного режима

Мы можем вернуться к корню проекта:

cd ..
Войдите в полноэкранный режим Выход из полноэкранного режима

Нам нужно добавить проект в рабочую область так же, как мы это делали с хостом:

package.json:

{
  // ...
  "workspaces": [
    "host",
    "remote" // Adicionar aqui
  ],
  // ...
}
Войдите в полноэкранный режим Выход из полноэкранного режима

Давайте добавим react и react-dom в проект:

yarn workspace remote add react react-dom
Войдите в полноэкранный режим Выход из полноэкранного режима

Плюс к некоторым зависимостям для разработки:

yarn workspace remote add -D webpack webpack-cli 
  webpack-dev-server html-webpack-plugin css-loader 
  source-map-loader style-loader ts-loader
Войдите в полноэкранный режим Выход из полноэкранного режима

Теперь нам нужно создать папки внутри проекта:

cd remote
mkdir src
mkdir public
cd ..
Войдите в полноэкранный режим Выход из полноэкранного режима

А также файлы App.tsx, index.tsx и index.html:

touch remote/src/App.tsx
Войдите в полноэкранный режим Выход из полноэкранного режима

remote/src/App.tsx:

import React from "react";

const App = (): JSX.Element => {
  return (
    <>
      <div>React Remote</div>
    </>
  );
};

export default App;
Войдите в полноэкранный режим Выход из полноэкранного режима
touch remote/src/index.tsx
Войдите в полноэкранный режим Выход из полноэкранного режима

remote/src/index.tsx:

import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App";

const container = document.getElementById("root");
const root = createRoot(container!);

root.render(<App />);
Войдите в полноэкранный режим Выход из полноэкранного режима
touch remote/public/index.html
Войдите в полноэкранный режим Выход из полноэкранного режима

remote/public/index.html:

<!DOCTYPE html>
<html lang="en">

<head> </head>

<body>
  <div id="root"></div>
</body>

</html>
Войдите в полноэкранный режим Выход из полноэкранного режима

На этом этапе нам нужно добавить конфигурационные файлы для webpack и typescript:

touch remote/tsconfig.json
Войдите в полноэкранный режим Выход из полноэкранного режима

remote/tsconfig.json:

{
  "compilerOptions": {
    "outDir": "./dist/",
    "noImplicitAny": true,
    "module": "commonjs",
    "target": "es5",
    "jsx": "react",
    "allowJs": true,
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true
  }
}
Войдите в полноэкранный режим Выход из полноэкранного режима
touch remote/webpack.config.js
Войдите в полноэкранный режим Выход из полноэкранного режима

remote/webpack.config.js:

const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: "./src/index",
  target: "web",
  mode: "development",
  devtool: "source-map",
  resolve: {
    extensions: [".jsx", ".js", ".tsx", ".ts", ".json"],
  },
  module: {
    rules: [
      {
        enforce: "pre",
        test: /.js$/,
        loader: "source-map-loader",
      },
      {
        test: /.(ts|tsx)$/,
        use: "ts-loader",
        exclude: /node_modules/,
      },
      {
        test: /.css$/i,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./public/index.html",
    }),
  ],
};
Войдите в полноэкранный режим Выход из полноэкранного режима

Нам также необходимо добавить скрипты в корневой и удаленный файлы package.json:

remote/package.json:

{
  // ...
  "scripts": {
    "start": "webpack-dev-server --port 3001"
  },
  // ...
}
Войдите в полноэкранный режим Выход из полноэкранного режима

package.json:

{
  // ...
  "scripts": {
    "start": "concurrently "yarn workspace host dev" "yarn workspace remote start""
  },
  // ...
}
Войдите в полноэкранный режим Выход из полноэкранного режима

И в завершение мы запускаем install для обновления зависимостей:

yarn
Войдите в полноэкранный режим Выход из полноэкранного режима

В этот момент ваша IDE (в случае печати, VS Code) может показать следующую ошибку в файле host/tsconfig.json:

Для разрешения просто добавьте элемент moduleResolution:

host/tsconfig.json:

{
  "compilerOptions": {
    // ...
    "moduleResolution": "node",
    "resolveJsonModule": true,
    // ...
  },
  // ...
}
Войдите в полноэкранный режим Выход из полноэкранного режима

Настройка федерации модулей

Именно на этом этапе произойдет волшебство!

Давайте начнем с REMOTE, нашего проекта React, с создания простого компонента, который будет передаваться и потребляться хостом:

remote/src/components/Button.tsx:

import React from "react";

const Button = (): JSX.Element => {
  return (
    <>
      <button>Remote Button</button>
    </>
  );
};

export default Button;
Войдите в полноэкранный режим Выход из полноэкранного режима

Нам также необходимо добавить некоторые настройки Webpack:

remote/webpack.config.js:

const HtmlWebpackPlugin = require("html-webpack-plugin");

const ModuleFederationPlugin =
  require("webpack").container.ModuleFederationPlugin;

module.exports = {
  entry: "./src/index",
  target: "web",
  mode: "development",
  devtool: "source-map",
  resolve: {
    extensions: [".jsx", ".js", ".tsx", ".ts", ".json"],
  },
  module: {
    rules: [
      {
        enforce: "pre",
        test: /.js$/,
        loader: "source-map-loader",
      },
      {
        test: /.(ts|tsx)$/,
        use: "ts-loader",
        exclude: /node_modules/,
      },
      {
        test: /.css$/i,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./public/index.html",
    }),
        new ModuleFederationPlugin({
      name: "remote",
      filename: "remoteEntry.js",
      exposes: {
        "./Button": "./src/components/Button",
      },
      shared: {
        react: {
          requiredVersion: false,
          singleton: true,
        },
      },
    }),
  ],
};
Войдите в полноэкранный режим Выход из полноэкранного режима

Далее мы настроим наш проект HOST с помощью Next.js. Для этого нам понадобится установить плагин:

yarn workspace host add @module-federation/nextjs-mf@2.3.1
Войдите в полноэкранный режим Выход из полноэкранного режима

У нас также есть некоторые изменения в next.config.js:

host/next.config.js:

/** @type {import('next').NextConfig} */
const nextConfig = {
  webpack5: true,
  reactStrictMode: true,
  webpack(config, options) {
    const { webpack, isServer } = options;
    config.experiments = { topLevelAwait: true };

    config.module.rules.push({
      test: /_app.js/,
      loader: "@module-federation/nextjs-mf/lib/federation-loader.js",
    });

    config.plugins.push(
      new webpack.container.ModuleFederationPlugin({
        remotes: {
          remote: "remote@http://localhost:3001/remoteEntry.js",
        },
        shared: {
          react: {
            singleton: true,
            eager: true,
            requiredVersion: false,
          },
        },
      })
    );
    return config;
  },
}

module.exports = nextConfig
Войдите в полноэкранный режим Выход из полноэкранного режима

И, наконец, давайте импортируем Button, открытую REMOTE в наш индекс:

host/pages/index.tsx:

import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
import dynamic from 'next/dynamic'; // new

 // new
const RemoteButton = dynamic(() => import('remote/Button'), {
  ssr: false,
});

export default function Home() {
  return (
    <div className={styles.container}>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      {/** new */}
      <RemoteButton />

      <main className={styles.main}>
        <h1 className={styles.title}>
          Welcome to <a href="https://nextjs.org">Next.js!</a>
        </h1>

        <p className={styles.description}>
          Get started by editing{' '}
          <code className={styles.code}>pages/index.js</code>
        </p>

        <div className={styles.grid}>
          <a href="https://nextjs.org/docs" className={styles.card}>
            <h2>Documentation &rarr;</h2>
            <p>Find in-depth information about Next.js features and API.</p>
          </a>

          <a href="https://nextjs.org/learn" className={styles.card}>
            <h2>Learn &rarr;</h2>
            <p>Learn about Next.js in an interactive course with quizzes!</p>
          </a>

          <a
            href="https://github.com/vercel/next.js/tree/canary/examples"
            className={styles.card}
          >
            <h2>Examples &rarr;</h2>
            <p>Discover and deploy boilerplate example Next.js projects.</p>
          </a>

          <a
            href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
            className={styles.card}
          >
            <h2>Deploy &rarr;</h2>
            <p>
              Instantly deploy your Next.js site to a public URL with Vercel.
            </p>
          </a>
        </div>
      </main>

      <footer className={styles.footer}>
        <a
          href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
          target="_blank"
          rel="noopener noreferrer"
        >
          Powered by{' '}
          <span className={styles.logo}>
            <Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
          </span>
        </a>
      </footer>
    </div>
  )
}

Войдите в полноэкранный режим Выход из полноэкранного режима

В этот момент статический разбор typescript будет предупреждать о неизвестном типе Button. Чтобы решить эту проблему, нам просто нужно создать определение типа:

host/remote.d.ts:

/// <reference types="react" />

declare module "remote/Button" {
  const Button: React.ComponentType;

  export default Button;
}
Войдите в полноэкранный режим Выход из полноэкранного режима

Все готово! Просто запустите проект…

yarn start
Войдите в полноэкранный режим Выход из полноэкранного режима

… и зайдите на HOST с адреса http://localhost:3000 и вы увидите отображение кнопки REMOTE.


Заключение

Вот и все. Если все прошло правильно при выполнении описанных выше шагов, то на вашей машине запущен проект Micro Frontend с использованием Module Federation. Круто, правда? И это тоже проще, чем кажется, верно?

Если вам понравился этот пост, если он был полезен для вас, оставьте свой отклик на него, а также воспользуйтесь возможностью следить за моим профилем на dev.to. Скоро я сделаю новые сообщения на эту тему.


Репозиторий на Github

https://github.com/patrickcoutinho/next-react-typescript-mfe


Ссылки

Примеры федерации модулей

Федерация модулей для Next.js

Док-ты по модулю Федерация

Создание приложений React с помощью федерации модулей и NextJS/React

Оцените статью
Procodings.ru
Добавить комментарий