Что такое микрофронтенд?
Микро-интерфейс предназначен для разделения различных функций на несколько подприложений в соответствии с разными размерами. Эти вспомогательные приложения загружаются через основное приложение (док). Сердцевина микропередка заключается в разборке, а после разборки она собирается.
Зачем использовать его?
- Разные команды разрабатывают одно и то же приложение, как использовать разные стеки технологий?
- Каждая команда может разрабатывать и развертывать независимо друг от друга
- Некоторый старый код приложения в проекте
Идея реализации микро-фронтенда состоит в том, чтобы разделить приложение на несколько суб-приложений и упаковать суб-приложения в библиотеки одно за другим. Когда путь переключается загружены разные подприложения. Таким образом, каждое подприложение является независимым, и стек технологий не нужно ограничивать! тем самым решив прежнее Комплексная совместная разработка
Несколько схем реализации микро-фронтендов
строить планы | описывать | преимущество | недостаток |
---|---|---|---|
Переадресация маршрута Nginx | Настройте обратный прокси-сервер через Nginx для сопоставления разных путей с разными приложениями. | Просто, быстро и легко настроить | Обновление браузера запускается при переключении приложений, что влияет на работу |
вложение iframe | Родительское приложение представляет собой одну страницу, а каждое дочернее приложение вкладывает iframe. | Реализация проста, а суб-приложения имеют свои песочницы, которые естественно изолированы и не влияют друг на друга. | Отображение стиля фрейма, совместимость и т. д. имеют ограничения, слишком простые и низкие |
форма пакета npm | Подпроекты выпускают исходный код в виде пакетов NPM; сборки и выпуски пакетов по-прежнему управляются базовым проектом, который интегрируется во время упаковки. | Упаковка и развертывание выполняются медленно и не могут быть развернуты отдельно. | Упаковка и развертывание выполняются медленно и не могут быть развернуты отдельно. |
Универсальный центральный пьедестал | Подпроекты могут использовать разные стеки технологий, подпроекты полностью независимы и не имеют зависимостей, единообразно управляются базовым проектом и комплектуются в соответствии с регистрацией, монтированием и выгрузкой DOM-узлов. | Неограниченный стек технологий, развернутый отдельно | Связь недостаточно гибкая |
Специальная центральная направляющая пьедестала | Между поднаправлениями используется один и тот же стек технологий, базовый проект и подпроект могут разрабатываться и развертываться отдельно, подпроект имеет возможность повторно использовать общую инфраструктуру базового проекта. | Существует множество способов связи, которые развертываются отдельно | Ограниченный стек технологий |
Текущая основная среда микроинтерфейса
- Single-SPA single-spa — это интерфейсное решение JavaScript для интерфейсных микросервисов (оно не обрабатывает изоляцию стилей, изоляцию выполнения js), которое реализует перехват маршрута и загрузку приложения.
- Основанный на Single-SPA, qiankun предоставляет более готовый API (single-spa + sandbox+ import-html-entry), который не зависит от стека технологий и прост в доступе (такой же простой, как iframe).
single-spa
И базовый, и подпроекты используют стек vue. На самом деле реакт тоже причина: база обеспечивает логику регистрации, а подприложение предоставляет три метода доступа к протоколу и форматы упаковки
пьедестал (вью)
В базе вызываем методы registerApplication и start, предоставленные нам single-spa 1. Параметр registerApplication имеет четыре appNameOrConfig, appOrLoadApp, activeWhen и customProps. Соответствует зарегистрированному имени подпроекта и некоторым конфигурациям. fn выполняется при загрузке подпроекта, выполнении правил и параметров связи.
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import {registerApplication,start} from 'single-spa';
// 动态加载url
async function loadScript(url){
return new Promise((resolve,reject)=>{
let script = document.createElement('script');
script.src = url;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
})
}
// singleSpa 缺陷 不够灵活 不能动态加载js文件
// 样式不隔离 没有js沙箱的机制
registerApplication('myVueApp',
async ()=>{
//当匹配成功的时候,加载子应用的js
await loadScript(`http://localhost:10000/js/chunk-vendors.js`);
await loadScript(`http://localhost:10000/js/app.js`)
return window.singleVue; // 子应用打包umd格式。bootstrap mount unmount
},
//当匹配到/vue的时候执行上面的方法
location => location.pathname.startsWith('/vue'),
)
//启动应用
start();
new Vue({
router,
render: h => h(App)
}).$mount('#app')
подпроект
Самое главное в подпроекте — предоставить три метода: начальную загрузку, монтирование, размонтирование и формат упаковки.
main.js
Здесь, с помощью предоставленного сообществомsingle-spa-vue
, реагировать на использованиеsingle-spa-react
. По умолчанию он экспортирует три метода.
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import singleSpaVue from 'single-spa-vue';
const appOptions = {
el:'#vue', // 挂载到父应用中的id为vue的标签中
router,
render: h => h(App)
}
const vueLifeCycle = singleSpaVue({
Vue,
appOptions
})
// 如果是微前端模式下,single-spa会在window上挂在一个singleSpaNavigate的属性。
// 这时候我们需要将public_path改成子项目中的地址。
if(window.singleSpaNavigate){
__webpack_public_path__ = 'http://localhost:10000/'
}
//这个是让子项目能够单独的运行
if(!window.singleSpaNavigate){
delete appOptions.el;
new Vue(appOptions).$mount('#app');
}
// 协议接入 我定好了协议 父应用会调用这些方法
export const bootstrap = vueLifeCycle.bootstrap;
export const mount = vueLifeCycle.mount;
export const unmount = vueLifeCycle.unmount;
配置子项目的打包方式
vue.config.js
module.exports = {
configureWebpack:{
output:{
library:'singleVue',
libraryTarget:'umd'
},
devServer:{
port:10000
}
}
}
Недостатки одноместного спа
- Как видно из рисунка выше, базу нужно вручную добавлять js при сопоставлении пути, если в подпроекте 10 000 js, то....
- Стили не изолированы
- Нет механизма для песочницы js
qiankun
Так же просто, как вставить iframe qiankun напрямую вставляет html в контейнер с помощью метода выборки, поэтому подпроекты должны разрешать междоменное
пьедестал
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import ElementUI from "element-ui";
import "element-ui/lib/theme-chalk/index.css";
Vue.use(ElementUI);
// 从qiankun中导出两个方法
import { registerMicroApps, start } from "qiankun";
const apps = [
{
name: "vueApp", // 应用的名字
entry: "//localhost:10000",//加载的html路径
container: "#vue", // 容器名
activeRule: "/vue", // 激活的路径
},
{
name: "reactApp",
entry: "//localhost:20000",
container: "#react",
activeRule: "/react",
},
];
registerMicroApps(apps); // 注册应用
start(); // 开启
new Vue({
router,
render: (h) => h(App),
}).$mount("#app");
подпроект
import Vue from 'vue'
import App from './App.vue'
import router from './router'
// Vue.config.productionTip = false
let instance = null
function render(props) {
instance = new Vue({
router,
render: h => h(App)
}).$mount('#app'); // 这里是挂载到自己的html中 基座会拿到这个挂载后的html 将其插入进去
}
if (window.__POWERED_BY_QIANKUN__) { // 动态添加publicPath
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
if (!window.__POWERED_BY_QIANKUN__) { // 默认独立运行
render();
}
// 子组件的协议就ok了
export async function bootstrap(props) {
};
export async function mount(props) {
render(props)
}
export async function unmount(props) {
instance.$destroy();
}
子项目打包配置
module.exports = {
devServer:{
port:10000,
// 需要允许跨域
headers:{
'Access-Control-Allow-Origin':'*'
}
},
configureWebpack:{
output:{
library:'vueApp',
libraryTarget:'umd'
}
}
}
связь приложения
- Передача данных основана на URL, но возможность передачи сообщений слабая
- Связь на основе CustomEvent
- Связь между основным и дочерним приложениями на основе реквизита
- Общение с глобальными переменными, Redux
схема изоляции css
Изоляция стилей между подприложениями:
- Динамическая таблица стилей Динамическая таблица стилей, когда приложение переключается, удаляет старый стиль приложения и добавляет новый стиль приложения.
Изоляция стиля между основным приложением и дочерним приложением:
- Префикс условного элемента BEM (Block Element Modifier)
- Создание неконфликтующих имен селекторов при объединении модулей CSS.
- Shadow Domнастоящая изоляция
механизм песочницы js
- Песочница моментальных снимков, запись моментальных снимков при подключении или отключении изолированной программной среды приложения и восстановление среды в соответствии со снимками при переключении (не может поддерживать несколько
пример)
- Прокси прокси песочница, не влияет на глобальную среду
Снимок песочницы
- 1. Снимок текущих свойств окна при активации
- 2. Сравните содержимое снимка с текущими свойствами окна при деактивации
- 3. Если свойство изменилось, сохраните его в ModifyPropsMap и восстановите свойство окна с помощью моментального снимка.
- 4. При следующей активации сделать снимок еще раз и восстановить окно с результатом последней модификации
class SnapshotSandbox{
constructor(){
this.proxy = window;
this.modifyPropsMap = {};
this.active();
}
active(){ // 激活
this.windowSnapshot = {}; // 拍照
for(const prop in window){
if(window.hasOwnProperty(prop)){
this.windowSnapshot[prop] = window[prop];
}
}
Object.keys(this.modifyPropsMap).forEach(p=>{
window[p] = this.modifyPropsMap[p];
})
}
inactive(){ // 失活
for(const prop in window){
if(window.hasOwnProperty(prop)){
if(window[prop] !== this.windowSnapshot[prop]){
this.modifyPropsMap[prop] = window[prop];
window[prop] = this.windowSnapshot[prop]
}
}
}
}
}
прокси-песочница
Каждое приложение создает прокси для проксирования окна.Преимущество заключается в том, что каждое приложение относительно независимо и не нуждается в непосредственном изменении глобальных свойств окна!
class ProxySandbox {
constructor() {
const rawWindow = window;
const fakeWindow = {}
const proxy = new Proxy(fakeWindow, {
set(target, p, value) {
target[p] = value;
return true
},
get(target, p) {
return target[p] || rawWindow[p];
}
});
this.proxy = proxy
}
}
Рефакторинг микроинтерфейса на основе проекта jquery
Предыстория проекта
используется в проектеbackbonejs
Фреймворк jq также содержитbootstrap
и ряд зависимостей. Хотя jquery — это шаттл, он немного громоздок в обслуживании. Отсюда и идея рефакторинга с микрофронтендами.
- Режим маршрутизации, используемый в проекте, — это хеш-режим.
Прежде всего, потому что этот проект представляет собой интерфейс, похожий на систему управления фоном, слева много меню. Если проект jquery используется в качестве подпроекта, изменения могут быть относительно большими, поэтому проект jq непосредственно используется в качестве базовой станции. При щелчке по левому маршруту субприложение загружается через механизм сопоставления, а затем встраивается в контейнер.
jquery
Сначала представим цянькунь
import {registerMicroApps, addGlobalUncaughtErrorHandler, start} from 'qiankun';
mian.js
function genActiveRule(routerPrefix) {
return location => location.hash.startsWith('#' + routerPrefix);
}
$(function() {
// 布局
createLayout();
// 路由
createRoute();
// 前往上次的页面
gotoLastPage();
registerMicroApps([
{
name: 'micro',
entry: '//xxx.com/home/vue/',
container: '#main-app',
activeRule: genActiveRule('/vue')
}
]);
addGlobalUncaughtErrorHandler(event => {
console.error(event);
const {message: msg} = event;
// 加载失败时提示
if (msg && msg.includes('died in status LOADING_SOURCE_CODE')) {
console.error('子应用加载失败,请检查应用是否可运行');
}
});
start();
monitorOrders.init();
});
Во-первых, содержимое записи — это адрес, к которому наше подприложение может получить доступ после упаковки и развертывания. Добавлено сообщение об ошибке, предоставленное qiankun.
Подпроект (vue)
main.js
import Vue from "vue";
import VueRouter from "vue-router";
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
// import "./public-path";
import App from "./App.vue";
import routes from "./routes";
Vue.use(VueRouter);
Vue.use(ElementUI);
Vue.config.productionTip = false;
let instance = null;
let router = null;
/**
* 渲染函数
* 两种情况:主应用生命周期钩子中运行 / 微应用单独启动时运行
*/
function render() {
console.log("window.__POWERED_BY_QIANKUN__", window.__POWERED_BY_QIANKUN__);
// 在 render 中创建 VueRouter,可以保证在卸载微应用时,移除 location 事件监听,防止事件污染
router = new VueRouter({
// 运行在主应用中时,添加路由命名空间 /vue
base: window.__POWERED_BY_QIANKUN__ ? "/vue" : "/",
mode: "hash",
routes,
});
// 挂载应用
instance = new Vue({
router,
render: (h) => h(App),
}).$mount("#app");
}
// 独立运行时,直接挂载应用
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
/**
* bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
export async function bootstrap() {
console.log("VueMicroApp bootstraped");
}
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export async function mount(props) {
console.log("VueMicroApp mount", props);
render(props);
}
/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
export async function unmount() {
console.log("VueMicroApp unmount");
instance.$destroy();
instance = null;
router = null;
}
vue.config.js
const path = require("path");
function resolve(dir) {
return path.join(__dirname, dir);
}
const stage = process.env.ENV === "testing";
const publicPath =
process.env.ENV === "production"
? xxxx:xxx
let output = {
library: "Micro",
// 将你的 library 暴露为所有的模块定义下都可运行的方式
libraryTarget: "umd",
// 按需加载相关,设置为 webpackJsonp_VueMicroApp 即可
jsonpFunction: `webpackJsonp_customerMicro`,
};
let cssExtract = {};
if (stage) {
output.filename = `js/[name].js?v=[hash]`;
output.chunkFilename = `js/[name].js?v=[hash]`;
cssExtract = {
filename: "css/[name].css?v=[hash]",
chunkFilename: "css/[name].css?v=[hash]",
};
}
module.exports = {
publicPath,
devServer: {
// 监听端口
port: 10200,
// 关闭主机检查,使微应用可以被 fetch
disableHostCheck: true,
// 配置跨域请求头,解决开发环境的跨域问题
headers: {
"Access-Control-Allow-Origin": "*",
},
},
css: {
extract: cssExtract,
},
configureWebpack: () => {
const conf = {
resolve: {
alias: {
"@": resolve("src"),
},
},
output: output,
};
return conf;
},
chainWebpack(config) {
config.plugins.delete("preload");
config.plugins.delete("prefetch");
config.module
.rule("vue")
.use("vue-loader")
.loader("vue-loader")
.tap((options) => {
options.compilerOptions.preserveWhitespace = true;
return options;
})
.end();
config.when(process.env.NODE_ENV === "development", (config) => config.devtool("cheap-source-map"));
},
};
Моя конфигурация выше предназначена для того, чтобы отличать тестовую среду от формальной среды. Вы также можете прийти в соответствии с реальной ситуацией. Вход на стороне базовой станции также может основываться на среде.
Коллекция серий ресурсов Micro Front-end
Спасибо всем
1. Если статья была вам полезна, поставьте мизинец, чтобы поставить лайк 👍, ваш лайк - движущая сила моего творчества.
2. Подпишитесь на официальный аккаунт "Front-end Elite"! Присылайте качественные статьи время от времени.