Что такое микрофронтенд
Микрофронтенд — это технические средства и стратегия методов, позволяющая нескольким командам совместно создавать современные веб-приложения путем независимой публикации функций.
Микро-интерфейс опирается на архитектурную концепцию микросервисов и делит огромное интерфейсное приложение на несколько независимых и гибких небольших приложений.Каждое приложение можно независимо разрабатывать, запускать и развертывать независимо, а затем эти небольшие приложения можно комбинировать. в полные приложения. Микро-интерфейс может не только интегрировать несколько проектов в один, но также уменьшить связь между проектами и улучшить масштабируемость проекта. быть меньше и гибче.
характеристика
- Независимость от стека технологийОсновная структура не ограничивает технологический стек приложения доступа, а подприложение может самостоятельно выбирать технологический стек.
- Самостоятельная разработка/развертываниеСклады между каждой командой независимы, развертываются отдельно и не зависят друг от друга
- Инкрементное обновлениеКогда приложение большое, обновление или рефакторинг технологии довольно проблематичны, в то время как микроприложения имеют характеристики постепенного обновления.
- При самостоятельном бегеСреды выполнения микроприложений не зависят друг от друга и имеют независимое управление состоянием.
- Повысить эффективностьЧем больше приложение, тем сложнее его поддерживать и тем менее эффективна совместная работа. Микроприложения могут быть хорошо разделены для повышения эффективности
В настоящее время доступны решения для микроинтерфейса
В настоящее время существуют следующие типы микро-интерфейсных решений:
на основеiframe
Полностью изолированное решение
Как фронтенд-разработчики, мыiframe
Уже очень знакомо, одно приложение может запускать другое приложение независимо. Он имеет существенные преимущества:
- Очень просто без каких-либо изменений
- Идеальная изоляция, JS и CSS — независимые операционные среды.
- Неограниченное использование, на странице можно разместить несколько страниц
iframe
совмещать бизнес
Конечно, недостатки тоже очень заметны:
- Состояние маршрутизации не может быть сохранено, и состояние маршрутизации теряется после обновления.
- Полная изоляция делает взаимодействие с подприложениями чрезвычайно сложным.
-
iframe
Всплывающее окно не может пробить себя - Все приложение загружается с полными ресурсами, и загрузка слишком медленная
Эти существенные недостатки также привели к созданию других решений.
на основеsingle-spa
схема захвата маршрута
single-spa
Переключение между подприложениями осуществляется путем перехвата маршрута, но метод доступа должен интегрировать собственный маршрут, что имеет определенные ограничения.
qiankun
Разработано на базе унифицированной платформы доступа Ant Financial Technology для облачных продуктов на основе микроинтерфейсной архитектуры. это правильноsingle-spa
Сделайте слой упаковки. В основном решеноsingle-spa
некоторые болевые точки и недостатки. пройти черезimport-html-entry
разбор пакетаHTML
Получите путь к ресурсу, а затем проанализируйте и загрузите ресурс.
Изменяя среду выполнения, он достигает JS 沙箱
,样式隔离
и другие характеристики.
Цзиндонmicro-app
строить планы
Цзиндонmicro-app
не следилsingle-spa
идеи, но заимствованные изWebComponent
мысль, черезCustomElement
в сочетании с обычайShadowDom
, который инкапсулирует микроинтерфейс в классwebComponents
компонентов, чтобы реализовать компонентный рендеринг микроинтерфейсов.
существуетVite
использование микрофронтендов на
мы начинаем с我们从 UmiJS 迁移到了 Vite
После этого микро-фронтенд также стал императивом, и в то время было исследовано множество решений.
почему это не работаетqiankun
qiankun
В настоящее время это основное микроинтерфейсное решение в сообществе. Хотя он совершенен и популярен, самая большая проблема в том, что он не поддерживаетVite
. это основано наimport-html-entry
Разобрать HTML для получения ресурсов, так какqiankun
черезeval
выполнить этиjs
содержание, при этомVite
серединаscript
Тип этикеткиtype="module"
, Который содержитimport/export
и другой код модуля, поэтому он сообщит об ошибке: не разрешено в не-type="module"
изscript
внутри использованиеimport
.
Сделав шаг назад к реализации, мы принялиsingle-spa
способ и использованиеsystemjs
В пути реализована схема микролобовой загрузки, да еще и на ямы наступило немало.single-spa
Нет дружественного учебника для доступа.Хотя есть много документов, большинство из них говорят о концепциях.В то время люди чувствовали, что это было эзотерическое чувство.
Позже я посмотрел его исходный код и обнаружил, что это то, что есть... Большая часть кода в нем разработана вокруг перехвата маршрутизации, и в документации нет ощущения величия. И мы не можем использовать его функцию перехвата маршрутизации, так зачем мы ее используем?
На уровне компонентаsingle-spa
Этот подход совсем не элегантен.
- перехватывает маршрутизацию с
react-router
Несовместимо с компонентным мышлением - Множество сложных конфигураций для методов доступа
- Одиночный экземпляр программы, а именно то же самое время, показано только субприложение
Подумав оsingle-spa
Мы можем реализовать компонентное микро-интерфейсное решение самостоятельно.
Как реализовать простое, прозрачное и компонентное решение
С помощью компонентного мышления реализовать микроприложение очень просто:Подприложение экспортирует метод, а основное приложение загружает подприложение и вызывает метод, передаваяElement
Параметры узла, подприложение получаетElement
узел, который будет его собственным компонентомappendChild
прибытьElement
на узле.
соглашение о типах
Перед этим нам нужно согласовать способ взаимодействия между основным приложением и под-приложением. В основном с помощью трех хуков для обеспечения правильного выполнения, обновления и удаления приложения.
Определение типа:
export interface AppConfig {
// 挂载
mount?: (props: unknown) => void;
// 更新
render?: (props: unknown) => ReactNode | void;
// 卸载
unmount?: () => void;
}
Экспорт вспомогательного приложения
С соглашениями о типах мы можем экспортировать подприложения:mount
,render
,unmount
для основного крючка.
React
Реализация подприложения:
export default (container: HTMLElement) => {
let handleRender: (props: AppProps) => void;
// 包裹一个新的组件,用作更新处理
function Main(props: AppProps) {
const [state, setState] = React.useState(props);
// 将 setState 方法提取给 render 函数调用,保持父子应用触发更新
handleRender = setState;
return <App {...state} />;
}
return {
mount(props: AppProps) {
ReactDOM.render(<Main {...props} />, container);
},
render(props: AppProps) {
handleRender?.(props);
},
unmount() {
ReactDOM.unmountComponentAtNode(container);
},
};
};
Реализация подприложения Vue:
import { createApp } from 'vue';
import App from './App.vue';
export default (container: HTMLElement) => {
// 创建
const app = createApp(App);
return {
mount() {
// 装载
app.mount(container);
},
unmount() {
// 卸载
app.unmount();
},
};
};
реализация основного приложения
React
выполнить
Его основной код составляет всего более десяти строк, в основном связанных с взаимодействием с подприложениями.(код обработки ошибок скрыт для удобства чтения):
export function MicroApp({ entry, ...props }: MicroAppProps) {
// 传递给子应用的节点
const containerRef = useRef<HTMLDivElement>(null);
// 子应用配置
const configRef = useRef<AppConfig>();
useLayoutEffect(() => {
import(/* @vite-ignore */ entry).then((res) => {
// 将 div 传给子应用渲染
const config = res.default(containerRef.current);
// 调用子应用的装载方法
config.mount?.(props);
configRef.current = config;
});
return () => {
// 调用子应用的卸载方法
configRef.current?.unmount?.();
configRef.current = undefined;
};
}, [entry]);
return <div ref={containerRef}>{configRef.current?.render?.(props)}</div>;
}
Готово, теперь реализованы операции загрузки, обновления и выгрузки основного приложения и подприложений. Теперь это компонент, который может отображать несколько различных подприложений одновременно, что болееsingle-spa
Много элегантности.
входной адрес суб-приложения, конечно, реальная ситуация будет основываться наdev
а такжеprod
Режимы дают разные адреса:
<MicroApp className="micro-app" entry="//localhost:3002/src/main.tsx" />
Vue
выполнить
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue';
const { entry, ...props } = defineProps<{ entry: string }>();
const container = ref<HTMLDivElement | null>(null);
const config = ref();
onMounted(() => {
const element = container.value;
import(/* @vite-ignore */ entry).then((res) => {
// 将 div 传给子应用渲染
const config = res.default(element);
// 调用子应用的装载方法
config.mount?.(props);
config.value = config;
});
});
onUnmounted(() => {
// 调用子应用的卸载方法
config.value?.unmount?.();
});
</script>
<template>
<div ref="container"></div>
</template>
Как заставить вспомогательные приложения работать независимо
single-spa
и многие другие решения, все монтируют переменную вwindow
Выше, судя по тому, находится ли переменная в среде микроинтерфейса, это очень неэлегантно. существуетESM
, мы можем пройтиimport.meta.url
Входящие параметры для оценки:
if (!import.meta.url.includes('microAppEnv')) {
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root'),
);
}
Модификация импорта импорта:
// 添加环境参数和当前时间避免被缓存
import(/* @vite-ignore */ `${entry}?microAppEnv&t=${Date.now()}`);
Совместимость с браузером
IE
Браузеры постепенно уходят из нашего поля зрения, основываясь наVite
, нам просто нужно поддержатьimport
Браузера функций достаточно. Конечно, если учестьIE
Это не невозможно для браузера, это очень просто: поместите приведенный выше кодimport
заменитьSystem.import
которыйsystemjs
,Слишкомsingle-spa
рекомендуемое использование.
браузер | Chrome | Edge | Firefox | Internet Explorer | Safari |
---|---|---|---|---|---|
import | 61 | 16 | 60 | No | 10.1 |
Dynamic import | 63 | 79 | 67 | No | 11.1 |
import.meta | 64 | 79 | 62 | No | 11.1 |
общий модуль
Наши дочерние компоненты должны использоватьmount
,unount
Режим? Ответ не обязательно, если наш стек технологийReact
если. Наше подприложение экспортирует только одинrender
достаточно. Это тоже самоеReact
Преимущество рендеринга в том, что дочернее приложение может потреблять родительское приложение.Provider
. Но есть предпосылка, что между двумя приложениямиReact
Должен быть один и тот же экземпляр, иначе будет сообщено об ошибке.
мы можем поставитьreact
,react-dom
,styled-componets
и другие часто используемые модули заранее упакованы вESM
модуль, а затем поместите его в файловую службу для использования.
ИзменятьVite
конфиг добавитьalias
:
defineConfig({
resolve: {
alias: {
react: '//localhost:8000/react@17.js',
'react-dom': '//localhost:8000/react-dom@17.js',
},
},
});
Таким образом, вы можете с удовольствием использовать тот жеReact
код вверх. Он также может извлекать общие модули между основным приложением и подприложениями, уменьшая общий объем приложения. Конечно если нетhttp2
Если это так, вам необходимо рассмотреть вопрос детализации.
онлайнCDN
строить планы:esm.sh
Еще одинimportmap
Схема, совместимость не очень, но за трендом будущее:
<script type="importmap">
{
"imports": {
"react": "//localhost:8000/react@17.js"
}
}
</script>
общение отца и сына
Компонентные микроприложения, которые могут передавать параметры и обмениваться данными, полностьюReact
Модель взаимодействия компонентов.
путь к ресурсу
существуетVite
изdev
В режиме статические ресурсы в подприложении обычно представлены следующим образом:
import logo from './images/logo.svg';
<img src={logo} />;
Путь к изображению:/basename/src/logo.svg
, он получит 404 при отображении в основном приложении. Потому что путь существует только в подприложении. нам нужно сотрудничатьURL
Использование модуля, так что путь будет предшествоватьorigin
Приставка:
const logoURL = new URL(logo, import.meta.url);
<img src={logoURL.href} />;
Конечно, использовать его таким образом громоздко, мы можем инкапсулировать его какVite
Плагин обрабатывает эту сцену автоматически.
синхронизация маршрута
Использование проектаreact-router
, то у него может быть проблема рассинхронизации маршрутизации, потому что это не то же самоеreact-router
пример. То есть между маршрутами нет связи.
существуетreact-router
Поддержка настройкиhistory
библиотеку, мы можем создать:
import { createBrowserHistory } from 'history';
export const history = createBrowserHistory();
// 主应用:路由入口
<HistoryRouter history={history}>{children}</HistoryRouter>;
// 主应用:传递给子应用
<Route
path="/child-app/*"
element={<MicroApp entry="//localhost:3002/src/main.tsx" history={history} />}
/>;
// 子应用:路由入口
<HistoryRouter basename="/child-app" history={history}>
{children}
</HistoryRouter>;
Последнее подприложение использует ту же копиюhistory
модуль. Конечно, это не единственная реализация, и это не элегантный способ, мы можем маршрутизировать instancenavigate
Передайте его подприложению, которое также может реализовать взаимодействие маршрута.
Примечание: вспомогательное приложениеbasename
должен быть связан с основным приложениемpath
Имя остается прежним. Здесь нужно изменитьVite
Конфигурацияbase
Поле:
export default defineConfig({
base: '/child-app/',
server: {
port: 3002,
},
plugins: [react()],
});
JS-песочница
потому что песочницаESM
Не поддерживается, так как модуль в среде выполнения не может быть динамически изменен.window
объект, а также не может быть введен новый глобальный объект.
в общемReact
,Vue
Кроме того, в проекте редко изменяются глобальные переменные, и очень важно хорошо выполнить проверку спецификации кода.
Изоляция стилей CSS
автоматическийCSS
Изоляция стилей обходится дорого, обычно мы рекомендуем, чтобы подприложения использовали разныеCSS
префикс, затем соответствиеCSS Modules
В принципе соответствует требованиям.
Развертывание пакета
Конфигурация Sub-App Vite
export default defineConfig({
base: '/basename/',
server: {
port: 3002,
},
build: {
rollupOptions: {
// 用于控制 Rollup 尝试确保入口块与基础入口模块具有相同的导出
preserveEntrySignatures: 'allow-extension',
input: 'src/main.tsx',
output: {
entryFileNames: '[name].js',
},
},
},
plugins: [react()],
});
Развертывание может быть основано наbase
Поместите их в разные каталоги и сопоставьте имена. Настроеноnginx
Правила переадресации в порядке. Мы можем унифицировать префикс маршрутизации подприложений, чтобы облегчитьnginx
Разделите основные приложения и настройте общие правила.
Например, размещение основного приложения вsystem
каталог, подприложения помещаются вapp-
Каталог, который начинается с:
location ~ ^/app-.*(..+)$ {
root /usr/share/nginx/html;
}
location / {
try_files $uri $uri/ /index.html;
root /usr/share/nginx/html/system;
index index.html index.htm;
}
преимущество
- ПростойЯдро состоит менее чем из 100 строк кода, дополнительная документация не требуется.
- гибкийПо соглашению о доступе это может быть прогрессивное улучшение
- ПрозрачныйНет схемы угона, больше логической прозрачности
- составнойКомпонентный рендеринг и передача параметров
- На основе ЕСМГотовность к будущему благодаря поддержке Vite
- обратная совместимостьДополнительное решение SystemJS, совместимое с браузерами более ранних версий.
есть пример
Пример кода находится наGithub
, заинтересованные друзья могутclone
Спускайся и учись.Поскольку наш стек технологийReact
, поэтому реализация основного приложения этого примера используетReact
.
- Компоненты микро-фронтенда (React):GitHub.com/min J i Eli U/No…
- Пример микро-фронтенда:GitHub.com/min J i Eli U/No…
Эпилог
Решение с микро-интерфейсом лучше всего подходит для командного сценария, и особенно важно создать решение, которым может управлять команда.
Использованная литература:
Практика миграции Vite
нажмитеДобавить Реагирующую группуобщаться с. Добро пожаловать на общедоступный номер:前端星辰