В приложениях уровня предприятия работа над приложением из единой кодовой базы оказывается огромной головной болью. Для этой цели была создана архитектура Micro-Frontend. Давайте погрузимся в мир Micro-Frontend и обсудим некоторые тонкости использования этого подхода.

Что такое микро-фронтенд?

Давайте проверим определение, которое выдает поиск Google:

Микроинтерфейсная архитектура – это подход к проектированию, при котором интерфейсное приложение разбивается на отдельные, независимые "микроприложения". свободно работая вместе.

Приложение Микроинтерфейс состоит из двух основных частей:

  • Контейнер
  • Подприложения

Контейнер определяет, когда каждое из дочерних приложений должно отображаться на экране. Вся бизнес-логика обрабатывается каждым из дочерних приложений.

Зачем использовать Micro-Frontend?

Использование архитектуры Micro-Frontend дает множество преимуществ:

  • Вы можете использовать разные фреймворки в каждом дочернем приложении.
  • Модификация или даже ошибки в одном дочернем приложении не влияют на другие подприложения.
  • Вы можете легко проводить A/B-тесты, чтобы максимизировать конверсию клиентов.
  • Облегчает совместную работу команд над проектом для команд (может размещаться как отдельный репозиторий для каждого дочернего приложения или моно- репо) и многое другое

Ключевые принципы микро-интерфейса

К этой архитектуре предъявляются два строгих требования:

  1. Микроприложения должны работать абсолютно независимо друг от друга, например, подприложение аутентификации никоим образом не должно полагаться на данные из подприложения продукта.
  2. Микроприложения могут взаимодействовать с контейнером, но взаимодействие должно быть минимальным и должно быть сделано насколько это возможно. Таким образом, даже если и контейнер, и подприложение используют один и тот же фреймворк, скажем, React, между ними должны передаваться не компоненты React, а некоторые универсальные функции и объекты. Это гарантирует, что во время серьезного рефакторинга либо микроприложений, либо контейнера нам не придется работать над другим.

Базовое приложение Micro-Frontend

Ладно, хватит болтать! Теперь пришло время запачкать руки созданием простого микроинтерфейсного приложения.

Создадим три папки для:

  • контейнер
  • тележка
  • продукты

Мы будем использовать faker для создания поддельных данных для корзины и продуктов. Чтобы установить библиотеку, откройте папку в командной строке, инициализируйте проект узла с помощью npm init и используйте npm install faker.

Внедрение Micro-Frontend было бы кошмаром почти для всех, но, к счастью, у нас есть подключаемый модуль Module Federation, доступный с webpack, который делает его частью торт. Чтобы установить все пакеты, связанные с webpack, используйте (в каждой папке):

npm install -D webpack webpack-cli html-webpack-plugin webpack-dev-server

Добавьте public/index.html во все три папки

<!-- cart/public/index.html -->
<div id="dev-cart"></div>
<!-- products/public/index.html -->
<div id="dev-products"></div>
<!-- container/public/index.html -->
<div id="product-list"></div>
<hr />
<div id="cart-items"></div>

Теперь настроим наш webpack.config.js:

const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    mode: 'development',
    devServer: {
        port: 3000,
    },
    plugins: [
        new HTMLWebpackPlugin({
            template: path.resolve(__dirname, 'public', 'index.html'),
        })
    ],
};

Скопируйте webpack.config.js в корень каждой папки, но убедитесь, что у вас разные номера портов для сервера разработки.

Теперь давайте настроим Module Federation:

корзина/webpack.config.js

// ...
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const packageJson = require('./package.json')
module.exports = {
    // ...
    plugins: [
        new ModuleFederationPlugin({
            name: 'cart', // name of the item being exposed (required in container)
            filename: 'remoteEntry.js', // name of the file
            exposes: {
                './Cart': './src/bootstrap' // actual file being exposed
            },
            shared: packageJson.dependencies, // to remove duplicate external modules loading in the container
        }),
        // ...
    ]
};

продукты/webpack.config.js

// ...
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const packageJson = require('./package.json')
module.exports = {
    // ...
    plugins: [
        new ModuleFederationPlugin({
            name: 'products', // name of the item being exposed (required in container)
            filename: 'remoteEntry.js', // name of the file
            exposes: {
                './ProductIndex': './src/bootstrap' // actual file being exposed
            },
            shared: packageJson.dependencies, // to remove duplicate external modules loading in the container
        }),
        // ...
    ]
};

контейнер/webpack.config.js

// ...
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const packageJson = require('./package.json')
module.exports = {
    // ...
    plugins: [
        new ModuleFederationPlugin({
            name: 'container', 
            remotes: {
                products: 'products@http://localhost:8000/remoteEntry.js', // importing products file from port 8000
                cart: 'cart@http://localhost:8001/remoteEntry.js', // importing cart file from port 8001
            },
        }),
        // ...
    ]
};

После настройки webpack нам нужно добавить нашу корзину, продукты и контейнер:

корзина/src/bootstrap.js

import faker from 'faker'
const mount = (element) => {
  // generating fake data
  element.innerHTML = `
  <p>You have ${faker.datatype.number(10)} items in your cart</p>
`
}
const mountPt = document.querySelector('#dev-cart')
if (process.env.NODE_ENV === 'development' && mountPt) {
  mount(document.querySelector('#dev-cart'))
}
export { mount } // sharing generic mount function

products/src/bootstrap.js

import faker from 'faker'
const PRODUCTS_COUNT = 5
const mount = (element) => {
    // generating fake data
    let productsArr = []
    for (let i = 0; i < PRODUCTS_COUNT; i++) {
        const product = faker.commerce.productName();
        productsArr.push(`<div>${product}</div>\n`)
    }
const products = productsArr.join('')
    element.innerHTML = products
}
const mountPt = document.querySelector('#dev-products')
if (process.env.NODE_ENV === 'development' && mountPt) {
    mount(mountPt)
}
export { mount } // sharing generic mount function

контейнер/src/bootstrap.js

import { mount as mountProducts } from 'products/ProductIndex'
import { mount as mountCart } from 'cart/Cart'
mountProducts(document.querySelector('#product-list'))
mountCart(document.querySelector('#cart-items'))

Наконец, создайте файл index.js в папке src каждого дочернего приложения и контейнера.

import('./bootstrap')

Создание этого файла с помощью динамического импорта абсолютно необходимо, поскольку webpack не может импортировать внешние пакеты без него (поскольку пакеты являются общими, поэтому они ведут себя иначе).

Теперь ваше приложение готово. Вы можете добавить следующий скрипт в раздел scripts файла package.json:

"scripts": {
    "dev": "webpack serve"
}

и вызовите npm run dev, чтобы запустить сервер webpack

Продукты (порт 8000)

Корзина (порт 8001)

Контейнер и все приложение (порт 3000)

Подведение итогов

В этой статье мы рассмотрели основы архитектуры микроинтерфейса. Надеюсь, это поможет вам в вашем пути развития :)

В настоящее время я работаю над проектом, использующим архитектуру Micro-frontend, не стесняйтесь проверить его:



Согласно исследованиям, записывая свои цели ручкой и бумагой, вы повышаете вероятность их достижения на 21%39%. Ознакомьтесь с этими блокнотами и журналами, чтобы облегчить путь к достижению своей мечты: https://www.amazon.com/Tapajyoti-Bose/e/B09VGDDHRR

Нужен фрилансер по фронтенд-разработке с самым высоким рейтингом, чтобы избавиться от проблем с разработкой? Свяжитесь со мной на Upwork

Хотите увидеть, над чем я работаю? Загляните на мой Персональный сайт и GitHub

Хотите подключиться? Свяжитесь со мной в LinkedIn

Первоначально опубликовано на https://dev.to 24 октября 2021 г.