Всем привет, я Сяо Хей.
Компания использует vue в качестве технологического стека и недавно столкнулась с необходимостью перенести функциональные модули исходной системы управления фоном в новую систему управления фоном. Изначально это было не слишком сложно, просто копировал и вставлял и модифицировал, но там столько ям, что я сразу впал в медитацию:
1. В новом фоне используется vue3, а в исходном фоне — vue2.
2. Новый фон имеет собственный набор схем управления правами входа в систему, а старый фон также имеет
3. Поскольку разница между vue3 и vue2 все еще относительно велика, vue3 эквивалентен перезаписи всего vue.Хотя он обратно совместим, скопировать и вставить его напрямую нереально (в основном потому, что я пытался скопировать модуль в прошлом.Красный цвет devtools ужасает)
Что делать, переписать модуль, написанный на vue2, на vue3 (удушающий)? Когда я собирался печатать на клавиатуре со слезами на глазах, я подумал о связанных статьях о микроинтерфейсе, которые я читал раньше.Почему бы мне не попробовать это, и тогда микроинтерфейс официально шагнул на яме
Что такое микрофронтенд?
Согласно Интернету и пониманию Сяо Хэя, микроинтерфейс应用分割,独立运行,独立部署
, оригинальный метод концентрации всех функций в одном проекте трансформируется в функцию, которая разделена на основной проект и несколько подпроектов в соответствии с бизнесом, каждый подпроект отвечает за свои функции и имеет возможность общаться с другие подпроекты и основной проект, Для достижения более подробной и легкой в управлении цели. В общем, микрофронтенды
Полное приложение разделено на основное приложение и одно или несколько микроприложений, которые независимы друг от друга и могут взаимодействовать друг с другом.
Как реализовать микрофронтенд?
Соблюдайте вышеуказанные условия, проще всего придумать iframe, вставьте два простейших iframe и его код связи ниже.
// parent.html
<div>我是parent</div>
<button id="parentBtn">parent btn</button>
<iframe src="./child.html" id="frame"></iframe>
<script>
function parentFunc(msg) {
console.log("parent 的方法:", msg)
}
var btn = document.querySelector("#parentBtn")
btn.addEventListener('click', function() {
console.log("我是parent的button")
console.log("我调用了:")
document.getElementById('frame').contentWindow.childFunc('parent');
})
</script>
// child.html
<div>我是child</div>
<button id="childBtn">child btn</button>
<script>
function childFunc(msg) {
console.log("child 的方法:", msg)
}
var btn = document.querySelector("#childBtn")
btn.addEventListener('click', function() {
console.log("我是child的button")
console.log("我调用了:")
parent.window.parentFunc('child');
})
</script>
Вышеупомянутые два фрагмента кода помещаются на локальный сервер следующим образом.
Затем нажмите две кнопки, вы можете общаться друг с другом и передавать параметры
Вышеуказанные два html должны быть запущены в среде с доменным именем, иначе будет сообщено об ошибке
Конечно, в этой миграции проекта я не использовал iframe для преобразования напрямую, а стоял на плечах гигантов, для преобразования я использовал микроинтерфейсный фреймворк под названием qiankun, потому что я не могу вставить код компании, я его создам ниже Проект vue3 и проект vue2, чтобы примерно восстановить то, как я преобразовал проект компании, и как заполнить ямы, с которыми я столкнулся
Микроинтерфейсный фреймворк qiankun
сначала вставитьофициальный сайт Цянькунь, вы можете сначала взглянуть на его введение и связанный с ним API.
Во-первых, используйте официальные леса vue для создания базового фонового интерфейса vue3 и базового фонового интерфейса vue2.Обратите внимание, что, поскольку vue3 упакован и использует vite, фреймворк qiankun не может использовать vue3 в качестве микроприложения, здесь наше основное приложение это vue3, микро-приложение vue2, то же самое, что я преобразовал Два проекта имеют одинаковую структуру, как показано ниже.
Для удобства всех вставьте адрес склада шаблона, который я построил
шаблон vue3:git ee.com/Jim PP/v UE3-…(Основное приложение, главное приложение должно установить qiankun)
шаблон vue2:git ee.com/Jim PP/v UE2-…(Микро приложение)
Вышеупомянутая ветка master — это проект, который может работать самостоятельно до преобразования, а ветка dev — это проект после окончательного преобразования.Конечно, также возможно построить его от начала до конца, но необходимо обеспечить что оба репозитория имеют функции маршрутизатора, хранилища и перехвата входа в систему.
Оба шаблона имеют этот интерфейс
1. Интерфейс входа
Кхм, это немного потрепанно, пожалуйста, не бейте меня, чтобы показать это, ха-ха
2. Левое меню и интерфейс просмотра маршрутизатора
Что ж, приступим к трансформации двух проектов на базе qiankun framework
Основное приложение запускает qiankun
Здесь я использую registerMicroApps официального сайта qiankun для регистрации микроприложений.
Создайте новую папку micros в папке src основного приложения и создайте новую в папке micros.index.js,app.js
// index.js
import NProgress from "nprogress";
import "nprogress/nprogress.css";
import {
registerMicroApps,
addGlobalUncaughtErrorHandler,
start,
} from "qiankun";// 微应用注册信息
import apps from "./app";
registerMicroApps(apps, {
beforeLoad: (app) => {
// 加载微应用前,加载进度条
NProgress.start();
console.log("before load", app.name);
return Promise.resolve();
},
afterMount: (app) => {
// 加载微应用前,进度条加载完成
NProgress.done();
console.log("after mount", app.name);
return Promise.resolve();
},
});
addGlobalUncaughtErrorHandler((event) => {
console.error(event);
const { message: msg } = event
if (msg && msg.includes("died in status LOADING_SOURCE_CODE")) {
console.error("微应用加载失败,请检查应用是否可运行");
}
});
export default start;
первыйyarn add nprogress
Установлена библиотека nprogress для отображения индикатора выполнения при загрузке микроприложения, здесь используется несколько официальных API.
-
registerMicroApps
: Содержит два параметра, первый параметр — некоторая регистрационная информация микроприложения, а второй параметр — глобальная ловушка жизненного цикла микроприложения. -
addGlobalUncaughtErrorHandler
: глобальный обработчик необработанных исключений, этот API также можно использовать для перехвата ошибок, когда микроприложение сообщает об ошибке. -
start
: метод, который мы используем для запуска qiankun, включая параметр, использование конкретного параметра подробно не описывается.
Чтобы просмотреть приведенный выше подробный API, нажмитездесь
// app.js
const apps = [
{
name: "vue-micro-app",
entry: "//localhost:8081",
container: "#micro-container",
activeRule: "#/vue2-micro-app",
},
];
export default apps;
app.js экспортирует вышеупомянутыйregisterMicroApps
Первый параметр - это массив объектов, где каждое поле функционирует следующим образом:
-
name
: Имя микроприложения, которое должно соответствовать этому имени при последующей модификации микроприложения -
entry
: доменное имя и порт работающего микроприложения, я использую локальный порт 8081 -
container
: для запуска микроприложения требуется dom-контейнер, который является идентификатором dom-контейнера, и должна быть возможность использовать класс -
activeRule
: активировать правило для запуска микроприложения, когда оно обнаружит, что URL-адрес содержит значение activeRule, микроприложение будет запущено.
После добавления двух вышеуказанных js возвращаемся к main.js, текущий main.js должен быть таким
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import '@/assets/main.css'
createApp(App).use(store).use(router).mount('#app')
Преобразование также очень простое, поместите микро в вышеindex.js
импортировать и запуститьstart函数
ты закончил
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import '@/assets/main.css'
import start from '@/micros'
createApp(App).use(store).use(router).mount('#app')
start()
Обновите браузер и обнаружите, что основное приложение ничем не отличается от того, что было до трансформации!
Основное приложение добавляет контейнер микроприложений и меню микроприложений.
Текущая структура кода меню основного приложения выглядит следующим образом.
<div class="nav" v-if="token">
<div class="menu">
<router-link to="/">Parent Home</router-link>
</div>
<div class="menu">
<router-link to="/about">Parent About</router-link>
</div>
</div>
Теперь добавим два меню, соответствующие домашнему и о подприложении.
<div class="nav" v-if="token">
<div class="menu">
<router-link to="/">Parent Home</router-link>
</div>
<div class="menu">
<router-link to="/about">Parent About</router-link>
</div>
<!--- 新添加 --->
<div class="menu">
<router-link to="/vue2-micro-app">Child Home</router-link>
</div>
<div class="menu">
<router-link to="/vue2-micro-app/about">Child About</router-link>
</div>
</div>
<div class="container">
<div class="header" v-if="token">Child Header</div>
<div class="router-view">
<router-view />
<!-- 新添加,微应用的容器 -->
<div id="micro-container"></div>
</div>
</div>
Я считаю, что вы тоже нашли это, есть больше выше вapp.js
изactiveRule
Соответствующее значение в поле (с удаленным #), потому что #/vue2-micro-app — это условие, запускающее запуск микро-приложения.
Это нужно для того, чтобы обновить наше микро-приложение, затем щелкните меню «Детский дом», вы найдете две ошибки.
Первый — отчет об ошибках между доменами, потому что наше основное приложение работает на порту 8080, а микроприложение — на порту 8081. Хорошо использовать nginx в качестве прокси позже.
Вторая ошибка связана с тем, что наше микро-приложение не трансформировалось, так чего же вы ждете, поторопитесь и трансформируйте микро-приложение
Трансформация микроприложений
На официальном сайте написано, запись микроприложения надо экспортироватьbootstrap
,mount
,unmount
Три хука жизненного цикла для вызова основного приложения в нужное время
Это main.js до трансформации микроприложения.
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import '@/assets/main.css'
Vue.config.productionTip = false
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
Преобразим main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import '@/assets/main.css'
Vue.config.productionTip = false
// 新增:用于保存vue实例
let instance = null;
// 新增:动态设置 webpack publicPath,防止资源加载出错
if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
/** * 新增: * 渲染函数 * 两种情况:主应用生命周期钩子中运行 / 微应用单独启动时运行 */
function render() {
// 挂载应用
instance = new Vue({
router,
store,
render: (h) => h(App),
}).$mount("#micro-app");}
/**
* 新增:
* 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;
}
// 新增:独立运行时,直接挂载应用
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
// 这是原本启动的代码
// new Vue({
// router,
// store,
// render: h => h(App)
// }).$mount('#app')
Обратите внимание, что в методе рендеринга я изменил параметр после $mount на
#micro-app
, это делается для того, чтобы различать корневой идентификатор index.html в основном приложении и микроприложении, поэтому index.html общей папки в микроприложении также следует изменить на микроприложение.
Затем необходимо преобразовать конфигурацию веб-пакета и добавить корневой каталог микроприложения.vue.config.js
документ
const path = require("path");
module.exports = {
devServer: {
// 监听端口
port: 8081,
// 关闭主机检查,使微应用可以被 fetch
disableHostCheck: true,
// 配置跨域请求头,解决开发环境的跨域问题
headers: {
"Access-Control-Allow-Origin": "*",
},
},
configureWebpack: {
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
},
},
output: {
// 微应用的包名,这里与主应用中注册的微应用名称一致
library: "vue-micro-app",
// 将你的 library 暴露为所有的模块定义下都可运行的方式
libraryTarget: "umd",
// 按需加载相关,设置为 webpackJsonp_VueMicroApp 即可
jsonpFunction: `webpackJsonp_vue-micro-app`,
},
},
};
Затем нам нужно изменить нашу маршрутизацию
if (window.__POWERED_BY_QIANKUN__) {
microPath = '/vue2-micro-app'
}
const routes = [
{
path: microPath + '/login',
name: 'login',
component: Login
},
{
path: microPath + '/',
redirect: microPath + '/home'
},
{
path: microPath + '/home',
name: 'Home',
component: Home
},
{
path: microPath + '/about',
name: 'About',
component: () => import( /* webpackChunkName: "about" */ '../views/About.vue')
}
]
router.beforeEach((to, from, next) => {
if (to.path !== (microPath + '/login')) {
if (store.state.token) {
next()
} else {
next(microPath + '/login')
}
}
else {
next()
}
})
Основное изменение в маршрутизации заключается в том, что к каждому пути добавляется переменная microPath, чтобы определить, был ли он изменен микроинтерфейсом. Соответствующая защита маршрутизации также должна добавлять переменную microPath. - скачки входа в приложение.
Наконец, перезапустите наше микро-приложение, а затем перейдите в наше основное приложение и нажмите меню «Детский дом».Если не случайно, вы получите тот же интерфейс, что и мой скриншот ниже.
Да, у вас получилось! Проект vue2 успешно встроен в vue3.
Тем не менее, вы также заметили, что я уже вошел в систему один раз, почему я должен войти в систему снова, поэтому в следующий раз мы должны использовать связь для решения этой проблемы.
Основное приложение и микросхема Micro App
Связь между приложениями, которую мы хотим использоватьqiankun
обрамленныйinitGlobalState和MicroAppStateActions
api, соответствующий API вводится следующим образом:
setGlobalState
:настраиватьglobalState
- Когда установлено новое значение, будет выполняться внутренний浅检查
, если отмеченоglobalState
Когда происходит изменение, запускается уведомление, чтобы уведомить всех观察者
функция.
onGlobalStateChange
:регистр观察者
функция - ответglobalState
изменение, вglobalState
Инициировать это, когда происходит изменение观察者
функция.
offGlobalStateChange
:Отмена观察者
функция - экземпляр больше не отвечаетglobalState
Разнообразие.
Итак, давайте снова преобразуем два проекта, первый — это micros/index.js основного приложения.
import {
registerMicroApps,
addGlobalUncaughtErrorHandler,
start,
initGlobalState // 新增
} from "qiankun";
const state = {}
const actions = initGlobalState(state);
export { actions }
Вышеуказанные добавленные и экспортированные действия, затем перейдите в login.vue
import { actions } from "@/micros"; //新增
const login = () => {
if (username.value && password.value) {
store.commit("setToken", "123456");
// 新增
actions.setGlobalState({globalToken: "123456"});
router.push({path: "/"});
}
};
Введены действия и добавлен метод action.setGlobalState.
Затем main.js подприложения
function render(props) {
console.log("子应用render的参数", props)
// 新增
props.onGlobalStateChange((state, prevState) => {
// state: 变更后的状态; prev 变更前的状态
console.log("通信状态发生改变:", state, prevState);
// 这里监听到globalToken变化再更新store
store.commit('setToken', '123456') }, true);
// 挂载应用
instance = new Vue({
router,
store,
render: (h) => h(App),
}).$mount("#micro-app");}
В методе рендеринга мы добавляем onGlobalStateChange, а второму параметру присваиваем значение true, чтобы при запуске микроприложения мы могли сразу увидеть только что установленный globalToken: 123456
Что ж, преобразование завершено, мы обновляем и снова входим в основное приложение, а затем нажимаем меню микроприложения, вы можете видеть, что микроприложению больше не нужно входить в систему, как показано ниже.
Вроде проблема еще есть, как отображается меню микроприложения? ? ?
Не бойтесь, последний шаг, пусть он будет решен за вас, дорогая, идея состоит в том, чтобы использовать его в микро-приложениях.window.__POWERED_BY_QIANKUN__
Чтобы судить, было ли оно запущено через qiankun, если да, то давайте напишем переменную и используем v-if, чтобы скрыть меню и заголовок микроприложения, не будет ли это сделано?
Выше приведено все содержание первой локальной разработки актуального фреймворка qiankun.Общая структура очень похожа на миграцию проекта, которую я сделал.Другие мелкие детали не затронуты.На самом деле в этой главе есть огромная яма. следующая статья«Микро-интерфейсный фреймворк проекта qiankun боевой (2) — выход на яму и развертывание»Возьмут все, чтобы развернуть упакованный проект и рассказать всем, где эта гигантская яма
использованная литература