Заявление об авторских правах: эта статья является оригинальной статьей блоггера и соответствует соглашению об авторских правах CC 4.0 BY-SA. Пожалуйста, приложите ссылку на оригинальный источник и это заявление для перепечатки.
Ссылка на эту статью:Гу Депэн.GitHub.IO/note/2020/0…
1. Предварительное напоминание
В прошлом посте уже было рассказано о том, что такое микро-фронтенд, студенты, которые мало что о нем знают, могут ознакомиться.Проектирование и практика архитектуры микроинтерфейса: происхождение
Случай в этой статье будет постоянно обновляться, чтобы стать проектом управления с открытым исходным кодом, звезда приветствуется, если вам нужна поддержка и новые функции, вы можете связаться со мной.
адрес проекта:GitHub.com/Гу Депэн/vu…
официальная ссылка qiankug:GitHub.com/UfanAccept/Kg…
В этой статье используются версия и метод после qiankun2.0.
2. Настоящий бой
1. Создайте проект
Поскольку основной проект и подпроекты разрабатываются с помощью vue, используйте vue-cli4 для создания проекта, создайте 2 проекта, основной проект и подпроект.
npm install -g @vue/cli @vue/cli-service-global
vue create vue-giant-master
vue create vue-giant-module
2. Подготовка основного проекта
Теперь приступайте к написанию проекта нормально.Я видел много демок и реальных проектов в Интернете до этого.У всех основной проект напрямую ставит код Qiankun в основной. Затем используйте v-if в app.vue, чтобы управлять отображением маршрута или загруженного подпроекта. Поскольку это проект фонового управления, в моей фактической разработке проекта в основном проекте есть много страниц маршрутизации, поэтому я чувствую, что этот метод не слишком удобен. Поэтому я использую обычный проект vue для запуска, а затем загружаю код qiankun на главной странице после входа в систему. Ниже приводится конкретная реализация.
<template>
<div class="panel" @click="doOnWeb" @keydown="doOnWeb">
<top-header class="panel-heder"></top-header>
<div class="panel-main">
<!-- 根据当前路由地址判断是子项目页面,还是主项目页面进行选择 -->
<router-view v-if="showView" />
<div v-else id="root-view"></div>
</div>
<main-menu ref="mainMenu" class="main-menu" v-show="showMenu"></main-menu>
<main-login ref="mainLogin"></main-login>
</div>
</template>
<script>
import TopHeader from '@/layout/components/Header'
import MainMenu from '@/layout/components/Menu'
import MainLogin from '@/layout/components/MainLogin'
// 导入乾坤函数
import { registerMicroApps, start } from 'qiankun'
import axios from '@/utils/request'
export default {
name: 'Layout',
components: {
TopHeader,
MainMenu,
MainLogin
},
data() {
return {
showMenu: false
}
},
computed: {
showView: function() {
return this.$route.path === '/home'
}
},
mounted() {
// 定义传入子应用的数据,方法和组件
const msg = {
data: this.$store.getters,
fns: [],
prototype: [{ name: '$axios', value: axios }]
}
// 注册子应用,可以根据登录后的权限加载对应的子项目
registerMicroApps(
[
{
name: 'module-app1',
entry: '//localhost:8081',
container: '#root-view',
activeRule: '/app1',
props: msg
},
{
name: 'module-app2',
entry: '//localhost:8082',
container: '#root-view',
activeRule: '/app2',
props: msg
}
],
{
beforeLoad: [
app => {
console.log('before load', app)
}
],
beforeMount: [
app => {
console.log('before mount', app)
}
],
afterUnmount: [
app => {
console.log('after unload', app)
}
]
}
)
// 启动微服务
start({ prefetch: true })
},
methods: {
toggleMenu() {
this.showMenu = !this.showMenu
this.$refs['mainMenu'].showTwoMenu = false
},
doOnWeb() {
this.$refs.mainLogin.doOnWeb()
}
}
}
</script>
Делая это, вы можете очень хорошо различать загрузку подпроекта и основного проекта. А также могут иметь общие дисплеи развертывания (например, меню и заголовок).
Обратите внимание на реализацию
1. Маршрут должен использовать режим истории, а переход на все страницы основного проекта должен осуществляться методом window.history.pushState(), иначе будет разлогиниться не подпроект, а контейнер #root-view подпроекта на странице будет v- Если удалить, qiankun сообщит об ошибке, а затем не будет загружен при переходе на страницу подпроекта.
2. При передаче методов или компонентов в подпроекты не используйте напрямую метод прямого размещения методов в массиве [функции], потому что такие имена методов будут упакованы и сжаты веб-пакетом во время фактического развертывания и упаковки, а под- проекты получат Имя метода будет неправильным, и подпроект не найдет этот метод при вызове метода основного проекта.
3. При передаче данных данных в этой статье используется геттер, который передает хранилище, а затем подпроект изначально передает значение в хранилище подпроекта при получении значения.При изменении значения основного проекта, он использует initGlobalState, предоставленный qiankun для передачи (в qiankun 1. Версия x использования qiankun не поддерживает связь после загрузки подпроекта, я использую метод привязки события события окна для передачи значения), и обновите его в хранилище в подпроекте.
Я также попытался передать хранилище непосредственно в подпроект, и реальная ситуация также будет передана, так что основной проект и подпроект эквивалентны общедоступному хранилищу, но страницы в подпроекте будут не следить за изменением магазина и менять,нужно вручную с помощью $forceUpdate(),я наверное смотрел исходники vuex.Похоже,это потому что он только создал монитор для изменений dom.Я не проверял это еще в глубине. Я сделаю подробное расследование позже. Если есть прямые друзья, вы можете написать мне немного.
дизайн маршрутизатора
import router from '../router'
import store from '../store'
import { Message } from 'element-ui'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { getUserToken } from '@/utils/user'
import Layout from '@/layout/index.vue'
NProgress.configure({
showSpinner: false
})
router.beforeEach(async (to, from, next) => {
NProgress.start()
const hasToken = getUserToken()
if (hasToken) {
if (to.path === '/login') {
next({
path: '/home'
})
NProgress.done()
} else {
const isLogin = store.getters.userInfo
if (isLogin) {
next()
} else {
try {
// 获取到实际有什么子项目后再加入到router权限中
await store.dispatch('user/getUserInfo')
router.addRoutes([
{
path: '/',
name: 'Layout',
component: Layout,
children: [
{
path: 'app1*'
}
]
}
])
next({
...to,
replace: true
})
} catch (error) {
Message.error(error || 'Has Error')
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
}
} else {
if (to.path === '/login') {
next()
NProgress.done()
} else {
next({
path: '/login'
})
}
}
})
router.afterEach(() => {
NProgress.done()
})
Когда пользователь не вошел в систему, страница входа перехватывается.При входе в систему оценивается, было ли получено разрешение.Если разрешение не было получено, перейдите к получению разрешения, а затем добавьте маршрут соответствующего подпроект к роутеру.
Поскольку моя страница подпроекта находится ниже страницы макета, если нет разрешения подпроекта, мне нужно добавить соответствующее разрешение
router.addRoutes([
{
path: '/',
name: 'Layout',
component: Layout,
children: [
{
path: 'app1*'
}
]
}
])
Написание подпроекта
Подпроект фактически представляет собой обычный vue-проект, методы и компоненты, полученные в main.js, монтируются на vue.prototype, а полученные данные инициализируются в хранилище.
import './public-path'
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import './plugins/element.js'
import '@/utils/permission.js'
Vue.config.productionTip = false
let instance = null
export async function bootstrap({ prototype }) {
prototype.map(p => {
Vue.prototype[p.name] = p.value
})
}
function initStore(props) {
props.onGlobalStateChange && props.onGlobalStateChange((value, prev) => {})
props.setGlobalState &&
props.setGlobalState({
ignore: props.name,
user: {
name: props.name
}
})
}
function render(props = {}) {
instance = new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
}
export async function mount(props) {
initStore(props)
if (props.data.userInfo.roles) {
store.commit('permission/SET_ROLES', props.data.userInfo.roles)
}
render()
}
export async function unmount() {
instance.$destroy()
instance = null
}
if (!window.__POWERED_BY_QIANKUN__) {
render()
}
Вам нужно создать public-path.js в том же каталоге, что и main.js
if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
дизайн маршрутизатора
Поскольку основной проект передал разрешения подпроектам. При инициализации маршрута вам нужно только разрешение на загрузку маршрута объекта.
import router from '@/router'
import store from '@/store'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
NProgress.configure({ showSpinner: false })
router.beforeEach(async (to, from, next) => {
NProgress.start()
const hasRoles = store.getters.routes && store.getters.routes.length > 0
if (hasRoles) {
next()
} else {
// get user info
const roles = await store.state.permission.roles
const accessRoutes = await store.dispatch(
'permission/generateRoutes',
roles
)
router.addRoutes(accessRoutes)
next({ ...to, replace: true })
}
})
router.afterEach(() => {
NProgress.done()
})
модификация vue.config.js
Вывод, куда нужно добавить файл
output: {
library: `${name}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${name}`
}
Подпроекты должны поддерживать кросс-домен, поэтому вам нужно добавить
headers: {
'Access-Control-Allow-Origin': '*'
}
разное
Выше приведен код ядра всего проекта, а остальные части — методы разработки конкретных vue-проектов, подробно описывать которые в этой статье не будем. Например: если пользователь не работает в течение определенного периода времени, появится диалоговое окно входа в систему, меню и другие функции.