предисловие
Недавно я увидел, как маленький друг в группе UED компании болтает о микро-фронтенде qiankun, и я тоже попал в яму от любопытства.
Что такое микрофронтенд?
Микро-интерфейс — это технические средства и стратегия методов, позволяющая нескольким командам совместно создавать современные веб-приложения путем независимой публикации функций.
qiankun
Микро-интерфейсное решение qiankun Ant Financial на основе единого спа-центра, доступное в производстве.
характеристика
- Основанный на пакете single-spa, он предоставляет более готовый API.
- Независимо от стека технологий, приложения любого стека технологий могут использоваться/доступны, будь то React/Vue/Angular/JQuery или другие фреймворки.
- Метод доступа HTML Entry позволяет получить доступ к микроприложениям так же легко, как и с помощью iframe.
- Изоляция стилей гарантирует, что стили между микроприложениями не будут мешать друг другу.
- Песочница JS, чтобы глобальные переменные/события не конфликтовали между микроприложениями.
- Предварительная загрузка ресурсов, которая предварительно загружает неоткрытые ресурсы микро-приложений во время простоя браузера, чтобы ускорить открытие микро-приложений.
Основная конструкция приложения
выбрать использованиеvue-cliОсновное приложение инициализировано, если вы не понимаете, то можете сами прочитать официальную документацию
Представлено в проектеqiankun:
$ yarn add qiankun # 或者 npm i qiankun -S
Зарегистрировать микроприложение
Определите микроприложения, которые необходимо загрузить
// src/micro/apps.ts
//此时我们还没有微应用,所以暂时为空
const apps: any = [
];
export default apps;
Зарегистрируйте микроприложение и выставьте метод
// src/micro/index.ts
import {
registerMicroApps,
addGlobalUncaughtErrorHandler,
start,
} from "qiankun";
import NProgress from "nprogress";
import { Message } from 'element-ui';
import 'nprogress/nprogress.css';
NProgress.configure({ parent: '.scrollbar.scroll' });
export default function (apps: []) {
registerMicroApps(apps, {
beforeLoad: () => {
// 加载微应用前,加载进度条
NProgress.start();
return Promise.resolve();
},
afterMount: () => {
NProgress.done();
return Promise.resolve();
},
});
addGlobalUncaughtErrorHandler((event: any) => {
const { msg } = event as any;
NProgress.done();
// 加载失败时提示
if (msg && msg.includes("died in status LOADING_SOURCE_CODE")) {
Message.error('微应用加载失败,请检查应用是否可运行');
}
});
start();
}
Поскольку наше микро-приложение может войти в систему, микро-приложение пользователя создается в соответствии с разрешениями левого меню пользователя. Таким образом, открытые методы и входные параметры удобны для входа в систему и вызова зарегистрированного микроприложения.
Запустить микроприложение
import startQiankun from "@/micro";
startQiankun(...); //在需要启动的地方调用传入数据就行
Вот что я начал с глобальной защиты маршрутизации для справки.
//router
import Vue from 'vue';
import VueRouter, { RouteConfig } from 'vue-router';
import store from "@/store";
import { getToken } from "@/utils/auth";
import startQiankun from "@/micro";
import apps from "@/micro/apps";
Vue.use(VueRouter);
const routes: Array<RouteConfig> = [
{
path: '/login',
name: 'login',
component: () => import('@/views/login/index.vue')
},
{
path: '/',
name: 'main',
component: () => import('@/views/Layout/index.vue'),
children: [
{
path: '',
name: 'Home',
component: () => import('@/views/Home.vue')
}
]
},
{
path: '*',
name: 'redirect',
redirect: '/'
}
];
const createRouter: any = () => new VueRouter({
mode: "history",
routes,
});
const router: any = createRouter()
/**
* 重置路由
*/
export function restRouter() {
router.matcher = createRouter().matcher;
}
const whiteList = ['login'];
router.beforeEach((to: any, from: any, next: any) => {
const token = getToken('token');
if (token) { //token存在
if (to.name === 'login') { //如果login直接跳转首页
return next({ path: '/' });
}
if (!store.state.hasInited) { //防止反复addRoutes预设的值
store.dispatch('addRouters').then((res) => {
router.addRoutes(res);
startQiankun(apps);
store.state.hasInited = true;
next({ ...to, replace: true });
})
return;
}
next();
} else if (whiteList.includes(to.name)) { //白名单直接放行
next();
} else { //token不存在
next({ path: '/login', query: { redirect: to.path } });
}
});
export default router;
Создание подприложения Vue
Настройте вспомогательные приложения, к которым необходимо получить доступ в основном приложении.
// micro/apps.ts
import app from "./shared"; //分享给子应用的数据
/*
* name: 微应用名称 - 具有唯一性
* entry: 微应用入口 - 通过该地址加载微应用
* container: 微应用挂载节点 - 微应用加载完成后将挂载在该节点上
* activeRule: 微应用触发的路由规则 - 触发路由规则后将加载该微应用
* props: 共享给微应用的数据
*/
const apps: any = [
{
name: "vue-project",
entry: "//localhost:10300",
container: "#app-qiankun",
activeRule: "/vue",
props: { app }
}
];
export default apps;
Настройка дополнительных приложений
После того, как основное приложение настроит зарегистрированное микроприложение, нам нужно настроить подприложение, чтобы подприложение могло получить доступ к основному приложению.
1. Конфигурация входа main.js подприложения vue
// public-path.js
if (window.__POWERED_BY_QIANKUN__) {
// 动态设置 webpack publicPath,防止资源加载出错
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
// main.js
import Vue from 'vue';
import App from './App.vue';
import VueRouter from "vue-router";
import './registerServiceWorker';
import routes from './router';
import store from './store';
import './public-path'
Vue.use(VueRouter)
Vue.config.productionTip = false;
let instance = null;
let router = null;
function render() {
router = new VueRouter({
// 运行在主应用中时,基础路由地址配置为 /vue
base: window.__POWERED_BY_QIANKUN__ ? "/vue" : "/",
mode: "history",
routes,
});
instance = new Vue({
router,
store,
render: (h) => h(App),
}).$mount("#app");
}
/**
* 不存在主应用时可直接单独运行
*/
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
export async function bootstrap() {
}
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export async function mount(props) {
console.log(props);
render(props);
}
/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
export async function unmount() {
instance.$destroy();
instance = null;
router = null;
}
2. Настройте стратегию упаковки webpack
// vue.config.js
const path = require("path");
module.exports = {
//配置静态文件host路径
publicPath: 'http://localhost:10300',
devServer: {
// 监听端口
port: 10300,
overlay: {
warnings: false,
errors: false
},
// 关闭主机检查,使微应用可以被 fetch
disableHostCheck: true,
// 配置跨域请求头,解决开发环境的跨域问题
headers: {
"Access-Control-Allow-Origin": "*",
}
},
configureWebpack: {
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
},
},
output: {
// 微应用的包名,这里与主应用中注册的微应用名称一致
library: "vue-project",
// 将你的 library 暴露为所有的模块定义下都可运行的方式
libraryTarget: "umd",
// 按需加载相关,设置为 webpackJsonp_vue-projec 即可
jsonpFunction: `webpackJsonp_vue-project`,
},
},
};
На данный момент наш микро-интерфейс полностью настроен, но на данный момент подключено только одно суб-приложение. По вышеприведенному коду можно сделать вывод и несколько ключевых моментов:
1. Зарегистрируйте субприложения в основном приложении:registerMicroApps
addGlobalUncaughtErrorHandler
start
Три важных API в qiankun используются вместе.
2. Запись подприложения зарезервирована для вызова основным приложением.bootstrap
mount
unmount
заявление о. а такжеwindow.__POWERED_BY_QIANKUN__
Определение.
3. Перенастройте стратегию упаковки подприложения.
Запустите эффекты основного приложения и дополнительных приложений:
/vue
Когда загружаются дополнительные приложения, включая/vue/about
также загрузил вспомогательное приложение/about
маршрутизация.
Откройте консоль, и вы увидите функцию хука жизненного цикла, которую мы выполнили.Создание подприложения React
Добавьте подприложения, к которым необходимо получить доступ в основном приложении.
// micro/apps.ts
import app from "./shared"; //分享给子应用的数据
/*
* name: 微应用名称 - 具有唯一性
* entry: 微应用入口 - 通过该地址加载微应用
* container: 微应用挂载节点 - 微应用加载完成后将挂载在该节点上
* activeRule: 微应用触发的路由规则 - 触发路由规则后将加载该微应用
* props: 共享给微应用的数据
*/
const apps: any = [
{
name: "vue-project",
entry: "//localhost:10300",
container: "#app-qiankun",
activeRule: "/vue",
props: { app }
},
{
name: "react-project",
entry: "//localhost:10100",
container: "#app-qiankun",
activeRule: "/react",
props: { app }
}
];
export default apps;
Конфигурация вспомогательного приложения
использоватьcreate-react-appИнициализировано приложение реакции
добавить в корневой каталог.env
файл, добавьте следующую конфигурацию
PORT=10100
BROWSER=none
1. Конфигурация входа в реакцию
// public-path.js
if (window.__POWERED_BY_QIANKUN__) {
// 动态设置 webpack publicPath,防止资源加载出错
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import "./public-path";
let root = document.getElementById("root");
function render() {
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
root
);
}
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
export async function bootstrap() {
console.log("ReactMicroApp bootstraped");
}
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export async function mount(props) {
console.log("ReactMicroApp mount", props);
root = document.getElementById("root");
render(props);
}
/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
export async function unmount() {
console.log("ReactMicroApp unmount");
//console.log(ReactDOM);
ReactDOM.unmountComponentAtNode(root);
root = null;
}
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
2. Настройте стратегию упаковки webpack
использоватьreact-app-rewiredИзмените конфигурацию упаковки.
//config-overrides.js
const path = require("path");
module.exports = {
webpack: (config) => {
// 微应用的包名,这里与主应用中注册的微应用名称一致
config.output.library = `react-project`;
// 将你的 library 暴露为所有的模块定义下都可运行的方式
config.output.libraryTarget = "umd";
// 按需加载相关,设置为 webpackJsonp_react-project 即可
config.output.jsonpFunction = `webpackJsonp_react-project`;
config.resolve.alias = {
...config.resolve.alias,
"@": path.resolve(__dirname, "src"),
};
return config;
},
devServer: function (configFunction) {
return function (proxy, allowedHost) {
const config = configFunction(proxy, allowedHost);
// 关闭主机检查,使微应用可以被 fetch
config.disableHostCheck = true;
// 配置跨域请求头,解决开发环境的跨域问题
config.headers = {
"Access-Control-Allow-Origin": "*",
};
// 配置 history 模式
config.historyApiFallback = true;
return config;
};
},
};
На данный момент также подключено подприложение react, вы можете запустить его, чтобы увидеть эффект
О доступе к приложениям без использования приложений webpack, Angular и др. говорить особо нечего, если вам интересно, вы можете найти информацию самостоятельно.
упакованные файлы развертывания nginx
Установка и использование nginx не представлены, если вы не знакомы с ним, можете самостоятельно использовать Baidu.
конфигурация nginx
#user nobody;
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html/dist;
index index.html index.htm;
try_files $uri $uri/ /index.html;
error_page 404 /index.html;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
server {
listen 10300;
server_name localhost;
location / {
root html/vue;
index index.html index.htm;
try_files $uri $uri/ /index.html;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
server {
listen 10100;nginx
server_name localhost;
location / {
root html/react;
index index.html index.htm;
try_files $uri $uri/ /index.html;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
server {
listen 10400;
server_name localhost;
location / {
root html/static;
index index.html index.htm;
try_files $uri $uri/ /index.html;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
онлайн из соображений безопасности,Access-Control-Allow-Origin
Не следует настраивать как *, нужно настроить указанное доменное имя. Краткое описание картинки вышеnginx
Конфигурация
-
html/dist
Для нашего основного приложения настроен порт 80,html/static
Простой проект без упаковки webpack. - Каждый сервер настроен с интерфейсными пакетами разных фреймворков.
- Обратите внимание, что настроенный адрес доступа должен быть таким же, как и в основных приложениях приложения.
прошлый обзор
- Написанная вручную Vue-версия компонента Upload
- Система операционной платформы усовершенствована для управления полномочиями кнопок (Vue) на основе полномочий меню.
- Backtop-компонент для Vue
демонстрационный адрес
GitHub.com/F one Q in RF/Пожалуйста…
Суммировать
Режимы маршрутизации всех вышеперечисленных приложений используются единообразно.history
режим, пробовал унифицированныйhash
Режим не удался, что делать, если два режима смешаны? попробовал унифицированныйhash
Режим или комбинация двух режимов - успешный копающий друг?
Мой уровень ограничен, передвигать кирпичи нелегко, пожалуйста, дайте мне еще совет!