Рекомендуемые статьи в прошлом
Для начала работы с Docker достаточно этой статьи
Введение в стек технологий
- микро интерфейс
- qiankun
- dockerЕсли вы этого не понимаете, пожалуйста, сначала прочитайте мое предыдущее введение, и вы поймете с первого взгляда.
- gitlab-ci/cdВот знание автоматизированного развертывания, вы можете узнать об этом
- nginx
Рекомендуется к сотрудничествуВидео объяснениепонимать быстрее
Что такое микрофронтенд
Микрофронтенд — это технические средства и стратегия методов, позволяющая нескольким командам совместно создавать современные веб-приложения путем независимой публикации функций.
Архитектура микроинтерфейса имеет следующие основные ценности:
-
Независимость от стека технологий Основной фреймворк не ограничивает технологический стек приложения доступа, а микроприложение имеет полную автономию.
-
Независимая разработка, независимое развертывание Хранилище микроприложений является независимым, а интерфейс и серверная часть могут разрабатываться независимо.После завершения развертывания основной фреймворк автоматически завершает обновление синхронизации.
-
Инкрементное обновление
Перед лицом различных сложных сценариев нам обычно сложно полностью обновить или рефакторить существующую систему, а микро-фронтенд — очень хорошее средство и стратегия для реализации инкрементного рефакторинга.
-
При самостоятельном беге Изоляция состояния между каждым микроприложением, состояние выполнения не передается
что такое цянькунь
qiankun – это готовый к работе фреймворк микроинтерфейса. Он основан на single-spa и обладает возможностями, необходимыми для систем микроинтерфейса, такими как песочница js, изоляция стилей, загрузчик HTML и предварительная загрузка. qiankun можно использовать в любой среде js, а доступ к микроприложениям так же прост, как встраивание системы iframe.
основная философия дизайна qiankun
Справочный адрес:qiankun.umijs.org/zh/guide
-
Простой
Поскольку микроприложение основного приложения может быть независимым от стека технологий, qiankun — это просто jQuery-подобная библиотека для пользователей.Вам необходимо вызвать несколько API-интерфейсов qiankun, чтобы завершить преобразование микроинтерфейса приложения. В то же время, благодаря дизайну HTML-записи и песочницы qiankun, доступ к микроприложениям так же прост, как с помощью iframe.
-
Независимость от развязки / технического стека
Основная цель микроинтерфейса — разобрать монолитное приложение на несколько слабосвязанных микроприложений, которые могут быть автономными, и многие проекты qiankun придерживаются этого принципа, например, запись HTML, песочница и взаимодействие между приложениями. Только таким образом микроприложения могут действительно развиваться и работать независимо.
Почему бы не использовать iframe
Справочный адрес:Woohoo.Yuque.com/Теряя его О, да/Го Кэйин 7…
Если не учитывать проблемы с опытом, iframes — почти идеальное решение для микроинтерфейса.
Самая большая особенность iframe заключается в том, чтобы обеспечить собственное решение жесткой изоляции браузера, будь то изоляция стиля, изоляция js и другие проблемы, которые могут быть идеально решены. Но его самая большая проблема заключается в том, что его изоляция не может быть нарушена, что приводит к невозможности совместного использования контекста между приложениями и, как следствие, к опыту разработки и опыту работы с продуктом.
- URL-адрес не синхронизирован. Состояние URL-адреса обновления браузера iframe теряется, кнопки «Назад» и «Вперед» использовать нельзя.
- Пользовательский интерфейс не синхронизирован, структура DOM не используется совместно. Представьте всплывающее окно со слоем маски в iframe в правом нижнем углу экрана, при этом мы требуем, чтобы всплывающее окно отображалось в центре браузера и автоматически центрировалось при открытии браузера. изменен размер.
- Глобальный контекст полностью изолирован, а переменные памяти не используются совместно. Для обеспечения связи и синхронизации данных внутренней и внешней систем iframe файл cookie основного приложения должен быть прозрачно передан в подприложения с разными корневыми доменными именами для достижения эффекта отсутствия входа в систему.
- медленный. Каждая запись подприложения — это процесс перестройки контекста браузера и перезагрузки ресурсов.
Некоторые проблемы решить легко (Задача 1), на некоторые проблемы мы можем закрыть глаза (Задача 4), а некоторые проблемы нам решить сложно (Задача 3) или даже невозможно решить (Задача 2). и эти неразрешимые проблемы принесут очень серьезные проблемы с опытом работы с продуктом, что в конечном итоге привело к тому, что мы отказались от решения iframe.
Основная ценность микрофронтенда
Woohoo.Yuque.com/Теряя его О, да/Го Кэйин 7…
идея проекта
Прежде чем говорить о конкретной технической реализации, давайте посмотрим, чего мы хотим достичь.
Схема микроинтерфейса
Основное приложение отвечает за управление статусом входа и отображением навигации.
Дочерние приложения загружаются динамически на основе кликов по навигации основного приложения.
Логика развертывания
Есть много идей для развертывания, я расскажу о том, как пробовал здесь:
-
Используйте только один контейнер nginx, разверните несколько приложений, прослушивая разные порты, а затем добавьте соответствующий прокси-сервер маршрутизации в подприложение в порт основного приложения.
Этот метод является самым простым, но не подходит для автоматического развертывания gitlab-ci/cd, поэтому я просто изначально протестировал реализацию микрофронтенда развертывания nginx.
-
Nginx использует множество контейнеров, каждый контейнер подвергается порту, затем соответствующий агент маршрутизации добавляется к подприложению главным приложением.
Этот метод может быть реализован, но он будет открывать несколько портов на сервере, безопасность будет снижена, а внешние также могут напрямую обращаться к подприложению через порт.
-
Используйте несколько контейнеров nginx, открывайте только порт основного приложения, основное приложение подключается к подприложению, а затем получает доступ через прокси-сервер nginx.
Этот способ самый идеальный, выставлять нужно только один порт, все агенты находятся между контейнерами, и они нечувствительны к внешнему миру.Ниже приведена иллюстрация реализации.
qiankun
установить цянькунь
$ yarn add qiankun # 或者 npm i qiankun -S
Зарегистрируйте микроприложение в основном приложении
import { registerMicroApps, addGlobalUncaughtErrorHandler, start } from 'qiankun';
const apps = [
{
name: 'ManageMicroApp',
entry: '/system/', // 本地开发的时候使用 //localhost:子应用端口
container: '#frame',
activeRule: '/manage',
},
]
/**
* 注册微应用
* 第一个参数 - 微应用的注册信息
* 第二个参数 - 全局生命周期钩子
*/
registerMicroApps(apps,{
// qiankun 生命周期钩子 - 微应用加载前
beforeLoad: (app: any) => {
console.log("before load", app.name);
return Promise.resolve();
},
// qiankun 生命周期钩子 - 微应用挂载后
afterMount: (app: any) => {
console.log("after mount", app.name);
return Promise.resolve();
},
});
/**
* 添加全局的未捕获异常处理器
*/
addGlobalUncaughtErrorHandler((event: Event | string) => {
console.error(event);
const { message: msg } = event as any;
// 加载失败时提示
if (msg && msg.includes("died in status LOADING_SOURCE_CODE")) {
console.error("微应用加载失败,请检查应用是否可运行");
}
});
start();
После регистрации информации о микроприложении, как только URL-адрес браузера изменится, будет автоматически запущена логика сопоставления qiankun, и все микроприложения, соответствующие правилу activeRule, будут вставлены в указанный контейнер, а микро- приложения будут вызываться по очереди для раскрытия ловушек жизненного цикла.
Если микроприложение не связано напрямую с маршрутом, вы также можете вручную загрузить микроприложение:
import { loadMicroApp } from 'qiankun';
loadMicroApp({
name: 'app',
entry: '//localhost:7100',
container: '#yourContainer',
});
Микро приложение
Микроприложения могут получить доступ к основному приложению qiankun без установки каких-либо дополнительных зависимостей.
1. Экспортируйте соответствующие хуки жизненного цикла
Микро-приложения необходимо экспортировать в свою собственную запись js (обычно запись js вашего настроенного веб-пакета)bootstrap,mount,unmountТри хука жизненного цикла для вызова основного приложения в нужное время.
import Vue from 'vue';
import VueRouter from 'vue-router';
import './public-path';
import App from './App.vue';
import routes from './routes';
import SharedModule from '@/shared';
Vue.config.productionTip = false;
let instance = null;
let router = null;
// 如果子应用独立运行则直接执行render
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
/**
* 渲染函数
* 主应用生命周期钩子中运行/子应用单独启动时运行
*/
function render(props = {}) {
// SharedModule用于主应用于子应用的通讯
// 当传入的 shared 为空时,使用子应用自身的 shared
// 当传入的 shared 不为空时,主应用传入的 shared 将会重载子应用的 shared
const { shared = SharedModule.getShared() } = props;
SharedModule.overloadShared(shared);
router = new VueRouter({
base: window.__POWERED_BY_QIANKUN__ ? '/manage/' : '/',
mode: 'history',
routes
});
// 挂载应用
instance = new Vue({
router,
render: (h) => h(App)
}).$mount('#app');
}
/**
* bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
export async function bootstrap() {
console.log('vue app bootstraped');
}
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export async function mount(props) {
console.log('vue mount', props);
render(props);
}
/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
export async function unmount() {
console.log('vue unmount');
instance.$destroy();
instance = null;
router = null;
}
/**
* 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
*/
export async function update(props) {
console.log('update props', props);
}
Приведенный выше код также ссылается наpublic-pathдокумент:
if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
В основном это решает проблему некорректных адресов, таких как скрипты, стили и изображения, динамически загружаемые микроприложениями.
2. Настройте инструмент упаковки для микроприложения.
В дополнение к предоставлению соответствующих ловушек жизненного цикла в коде, чтобы основное приложение могло правильно идентифицировать часть информации, предоставляемой микроприложением, инструмент упаковки микроприложений должен добавить следующую конфигурацию:
веб-пакет:
const packageName = require('./package.json').name;
module.exports = {
publicPath: '/system/', //这里打包地址都要基于主应用的中注册的entry值
output: {
library: 'ManageMicroApp', // 库名,与主应用注册的微应用的name一致
libraryTarget: 'umd', // 这个选项会尝试把库暴露给前使用的模块定义系统,这使其和CommonJS、AMD兼容或者暴露为全局变量。
jsonpFunction: `webpackJsonp_${packageName}`,
},
};
Резюме ключевых моментов
-
Конфигурация при регистрации основного приложения
const apps = [ { name: 'ManageMicroApp', entry: '/system/', // http://localhost/system/ 这里会通过nginx代理指向对应的子应用地址 container: '#frame', activeRule: '/manage', }, ]Когда основное приложение регистрирует микроприложение,
entryможет быть относительным путем,activeRuleне может иentryТо же самое (иначе главная страница приложения обновляется и оно становится микроприложением) -
основа маршрутизации vue
router = new VueRouter({ base: window.__POWERED_BY_QIANKUN__ ? '/manage/' : '/', mode: 'history', routes });Если он вызывается основным приложением, база маршрута
/manage/ -
конфигурация упаковки webpack
module.exports = { publicPath: '/system/', };для
webpackвстроенные микроприложения, микроприложенияwebpackупакованныйpublicPathнеобходимо настроить как/system/, иначе микроприложениеindex.htmlможет запросить правильно, но микроприложенияindex.htmlвнутриjs/cssпуть не пройдет/system/.
На этом мы завершили настройку микрофронтенда, и следующим шагом будет настройка nginx.
Конфигурация производственной среды Nginx
Сначала повесьте конфигурацию nginx основного приложения.
server {
listen 80;
listen [::]:80 default_server;
server_name localhost;
root /usr/share/nginx/html;
location / {
try_files $uri $uri/ /index.html;
index index.html;
}
# 前面我们配置的子应用entry是/system/,所以会触发这里的代理,代理到对应的子应用
location /system/ {
# -e表示只要filename存在,则为真,不管filename是什么类型,当然这里加了!就取反
if (!-e $request_filename) {
proxy_pass http://192.168.1.2; # 这里的ip是子应用docker容器的ip
}
# -f filename 如果 filename为常规文件,则为真
if (!-f $request_filename) {
proxy_pass http://192.168.1.2;
}
# docker运行的nginx不识别localhost的 所以这种写法会报502
# proxy_pass http://localhost:10200/;
proxy_set_header Host $host;
}
location /api/ {
proxy_pass http://后台地址IP/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
Взгляните на под-приложение
server {
listen 80;
listen [::]:80 default_server;
server_name _2;
root /usr/share/nginx/html;
# 这里必须加上允许跨域,否则主应用无法访问
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
location / {
try_files $uri $uri/ /index.html;
index index.html;
}
location /api/ {
proxy_pass http://后台地址IP/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
конфигурация докерфайла
Вот посмотрите на под-приложение
# 直接使用nginx镜像
FROM nginx
# 把上面配置的conf文件替换一下默认的
COPY nginx.conf /etc/nginx/nginx.conf
# nginx默认目录下需要能看见index.html文件
COPY dist/index.html /usr/share/nginx/html/index.html
# 再回头看一下部署逻辑图和qiankun注意点,必须要把所有的资源文件放到system文件下index.html才能正确加载
COPY dist /usr/share/nginx/html/system
Посмотрите еще раз на основное приложение
# 这里主应用没有直接使用nginx,因为nginx反向代理的/api/会出现404的问题,原因未知!
FROM centos
# 安装nginx
RUN yum install -y nginx
# 跳转到/etc/nginx
WORKDIR /etc/nginx
# 替换配置文件
COPY nginx.conf nginx.conf
# 跳转到/usr/share/nginx/html
WORKDIR /usr/share/nginx/html
# 主应用正常打包,所以直接把包放进去就行
COPY dist .
# 暴露80端口
EXPOSE 80
# 运行nginx
CMD nginx -g "daemon off;"
конфигурация gitlab-ci/cd
Давайте сначала посмотрим на приложение,только суть
image: node
stages:
- install
- build
- deploy
- clear
cache:
key: modules-cache
paths:
- node_modules
- dist
安装环境:
stage: install
tags:
- vue
script:
- npm install yarn
- yarn install
打包项目:
stage: build
tags:
- vue
script:
- yarn build
部署项目:
stage: deploy
image: docker
tags:
- vue
script:
# 通过dockerfile构建项目的镜像
- docker build -t rainbow-system .
# 如果存在之前创建的容器先删除
- if [ $(docker ps -aq --filter name=rainbow-admin-system) ];then docker rm -f rainbow-admin-system;fi
# 通过刚刚的镜像创建一个容器 给容器指定一个网卡rainbow-net,这个网卡是我们自定义,创建方式后面会说,然后给定一个ip
- docker run -d --net rainbow-net --ip 192.168.1.2 --name rainbow-admin-system rainbow-system
清理docker:
stage: clear
image: docker
tags:
- vue
script:
- if [ $(docker ps -aq | grep "Exited" | awk '{print $1 }') ]; then docker stop $(docker ps -a | grep "Exited" | awk '{print $1 }');fi
- if [ $(docker ps -aq | grep "Exited" | awk '{print $1 }') ]; then docker rm $(docker ps -a | grep "Exited" | awk '{print $1 }');fi
- if [ $(docker images | grep "none" | awk '{print $3}') ]; then docker rmi $(docker images | grep "none" | awk '{print $3}');fi
Снова посмотрите на основное приложение, исключите повторение и сосредоточьтесь непосредственно на ключевых моментах.
部署项目:
stage: deploy
image: docker
tags:
- vue3
script:
- docker build -t rainbow-admin .
- if [ $(docker ps -aq --filter name=rainbow-admin-main) ];then docker rm -f rainbow-admin-main;fi
# 给容器指定一个网卡rainbow-net,然后给定一个ip,然后通过--link与之前创建的子应用连通,重点!
- docker run -d -p 80:80 --net rainbow-net --ip 192.168.1.1 --link 192.168.1.2 --name rainbow-admin-main rainbow-admin
Вышеупомянутая пользовательская сетевая карта докера, сгенерированная команда выглядит следующим образом:
$ docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 rainbow-net
Суммировать
До сих пор мы реализовали автоматическое развертывание qiankun+docker и gitlab-ci/cd.Мы столкнулись со многими ямами, а затем нашли относительно разумное решение.Если у вас есть какие-либо вопросы, добро пожаловать в обсуждение.