-- qiankun-master 为主应用 -- sub-app1 为子应用 -- sub-app2 为子应用
-- 切换到qiankun-master
cd qiankun-master
-- 安装依赖
npm run install:all
-- 启动项目
npm run start
Techniques, strategies and recipes for building a modern web app with multiple teams that can ship features independently. -- Micro Frontends
微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。
微前端架构的核心价值:
-
技术栈无关
主框架不限制接入应用的技术栈,微应用具备完全自主权 -
独立开发、独立部署
微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新 -
增量升级
在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略 -
独立运行
每个微应用之间状态隔离,运行时状态不共享
官方介绍:qiankun 是一个基于 single-spa 的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。 具体可看官方文档
本次demo基于vue-cli4创建项目
前提条件: 已安装node和vue-cli
- 创建一个新的文件夹 命名如 qiankun-demo
- 切换到qiankun-demo目录下创建一个主应用,运行
vue create qiankun-master
创建vue项目教程 - 在qiankun-master同级目录创建若干子应用, 运行
vue create sub-app1
vue create sub-app2
安装依赖 前端工程化并行解决方案(同时跑多个命令)
npm install concurrently -S
主应用 app.vue
根据路由判断显示不同界面
<template>
<div id="root" class="mainApp">
<template v-if="!isChildApp">
<router-view/>
</template>
<template v-show="isChildApp" >
<wu-layoutMain :loading="loading" :isChildApp="isChildApp" :content="content"></wu-layoutMain>
</template>
</div>
</template>
<script>
computed: {
isChildApp() {
if(this.$route.path.match('sub-app')){
return true;
}else{
return false;
}
}
}
</script>
main.vue 框架布局菜单等
<!--内容区域-->
<Content class="main-content-con">
<Layout class="main-layout-con">
<!--面包屑导航-->
<div class="bread-crumb-wrapper">
<Breadcrumb :style="{fontSize:'14px'}">
<BreadcrumbItem
v-for="item in breadCrumbList"
:to="item.to"
:key="`bread-crumb-${item.name}`"
>
<common-icon style="margin-right: 4px;" :type="item.icon || ''"/>
{{showTitle(item)}}
</BreadcrumbItem>
<!--<BreadcrumbItem to="/">Home</BreadcrumbItem>-->
<!--<BreadcrumbItem to="/components/breadcrumb">Components</BreadcrumbItem>-->
<!--<BreadcrumbItem>Breadcrumb</BreadcrumbItem>-->
</Breadcrumb>
</div>
<Content class="content-wrapper">
<!--<router-view/>-->
<template v-if="!isChildApp">
<!--//不是子应用就调用路由-->
<router-view/>
</template>
<template v-show="isChildApp">
<!--//子应用就加载 content-->
<div id="router-view" v-html="content"></div>
</template>
</Content>
</Layout>
</Content>
main.js 传递子应用数据 和 注册子应用等
/**
* **事件总线EventBus,用于通信。
* 在Vue中可以使用 EventBus 来作为沟通桥梁的概念,就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件
*/
Vue.prototype.$EventBus = new Vue()
import {registerMicroApps,runAfterFirstMounted, setDefaultMountApp, start} from 'qiankun';
import fetch from 'isomorphic-fetch';
let app = null;
let count1 = 0;
let count = 0;
function render({ appContent, loading }) {
/*examples for vue*/
if (!app) {
app = new Vue({
el: '#container',
router,
store,
i18n,
data() {
return {
content: appContent,
loading,
};
},
render(h) {
return h(App, {
props: {
content: this.content,
loading: this.loading,
},
});
},
});
} else{
app.content = appContent;
app.loading = loading;
}
}
function genActiveRule(routerPrefix) {
return location => location.pathname.startsWith(routerPrefix);
}
render({ loading: true });
// 支持自定义获取请参阅: https://github.com/kuitos/import-html-entry/blob/91d542e936a74408c6c8cd1c9eebc5a9f83a8dc0/src/index.js#L163
const request = url =>
fetch(url, {
referrerPolicy: 'origin-when-cross-origin',
});
//注册子应用
registerMicroApps([
{
name: 'vue sub-app1',
entry: '//localhost:7100/sub.html',
render,
activeRule: genActiveRule('/sub-app1')
},
{
name: 'vue sub-app2',
entry: '//localhost:7101',
render,
activeRule: genActiveRule('/sub-app2')
},
// { name: 'vue admin', entry: '//localhost:9428', render, activeRule: genActiveRule('/admin') },
],
{
beforeLoad: [
app => {
console.log('before load', app);
},
],
beforeMount: [
app => {
console.log('before mount', app);
},
],
afterMount: [
app => {
console.log('after mount', app);
},
],
afterUnmount: [
app => {
console.log('after unload', app);
app.render({appContent: '', loading: false});
},
],
},
{
fetch: request,
},
);
/**
* @description 设置哪个子应用程序在主加载后默认处于活动状态
* @param defaultAppLink: string 跳转链接
*/
setDefaultMountApp('/home');
/**
* @description 第一个应用构建完成后执行
* @param 要执行的函数
*/
runAfterFirstMounted(() => console.info('first app mounted'));
/**
* @description 启动主应用
* @param prefetch 是否在第一次安装子应用程序后预取子应用程序的资产,默认为 true
* @param jsSandbox 是否启用沙盒,当沙盒启用时,我们可以保证子应用程序是相互隔离的,默认为 true
* @param singular 是否在一个运行时只显示一个子应用程序,这意味着子应用程序将等待挂载,直到卸载之前,默认为 true
* @param fetch 设置一个fetch function,默认为 window.fetch
*/
start();
router相关
import Vue from 'vue'
import Router from 'vue-router'
import routes from './routers'
import menus from './menus'
import childApps from './childApps'
let allMenus = routes.concat(menus)
import store from '@/store'
// import iView from 'iview'
import { setToken, getToken, canTurnTo, setTitle } from '@/libs/util'
import config from '@/config'
import routers from "@/router/routers";
const { homeName } = config
Vue.use(Router)
const router = new Router({
routes:allMenus,
mode: 'history',
base: process.env.BASE_URL,
})
const LOGIN_PAGE_NAME = 'login'
//路由白名单之:子应用路径
const subApp_ROUTE = ['/sub-app1']
router.beforeEach((to, from, next) => {
// iView.LoadingBar.start()
const path = to.path;
const token = getToken()
// console.log(to,"=====路由对象")
// if(subApp_ROUTE){
// next();
// }
if(token && to.name === undefined){
//已登录,子页面以及未知页面
next() // 跳转
} else if (!token && to.name !== LOGIN_PAGE_NAME) {
// 未登录且要跳转的页面不是登录页
next({
name: LOGIN_PAGE_NAME // 跳转到登录页
})
} else if (!token && to.name === LOGIN_PAGE_NAME) {
// 未登陆且要跳转的页面是登录页
next() // 跳转
} else if (token && to.name === LOGIN_PAGE_NAME) {
// 已登录且要跳转的页面是登录页
next({
name: homeName // 跳转到homeName页
})
} else {
// 已登录 要跳转的 页面不是 登录页面
if (store.state.user.hasGetInfo) {
// 已经含有用户信息
// vuex 中state的user 模块 hasGetInfo 为true
// turnTo(to, store.state.user.access, next) //静态权限遗留,可不用
next()
} else {
// 不含有 用户信息 ,请求获取用户相关信息
//dispatch:与commit()作用相同 含有异步操作, 例如向后台提交数据,写法: this.$store.dispatch('mutations方法名',值)
store.dispatch('getUserInfo').then(user => {
// 拉取用户信息,通过用户权限和跳转的页面的name来判断是否有权限访问;access必须是一个数组,如:['super_admin'] ['super_admin', 'admin']
// turnTo(to, user.access, next) //静态权限遗留,可不用
next();
}).catch(() => {
setToken('')
next({
name: 'login'
})
})
}
}
})
router.afterEach(to => {
//根据当前跳转的路由设置显示在浏览器标签的title
setTitle(to, router.app)
// iView.LoadingBar.finish()
window.scrollTo(0, 0)
})
export default router
https://qiankun.umijs.org/zh/faq