这段时间前端微服务异常的火爆,刚好公司也有类似的需求,之前的项目我都是分模块打包,同时,主页中嵌入iframe。刚好看到qiankun想要常识下做一个dome,看下运行效果如何
个人体会:可以拥有不同语言的子应用,子应用可以脱离主应用单独部署。但是,如果子应用之间相互交互部分组件的话,因为不是在一个项目包里面,所以,这个可能就无法实现。
iframe的缺点
- url 不同步。浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用。
- UI 不同步,DOM 结构不共享。想象一下屏幕右下角 1/4 的 iframe 里来一个带遮罩层的弹框,同时我们要求这个弹框要浏览器居中显示,还要浏览器 resize 时自动居中。
- 全局上下文完全隔离,内存变量不共享。iframe 内外系统的通信、数据同步等需求,主应用的 cookie 要透传到根域名都不同的子应用中实现免登效果。
- 慢。每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程。
一、主应用
主应用不限技术栈,只需要提供一个容器 DOM,然后注册微应用并 start 即可。
1、创建主应用项目 qian_kun_test
2、安装 qiankun
$ yarn add qiankun # 或者 npm i qiankun -S
3、在主应用中注册微应用
1、根目录src下新建文 microapplication/app.js
// 微应用的一些注册信息
const apps = [
{
name: 'app1', // 子应用app name 推荐与子应用的package.json的name一致。微应用的名称,微应用之间必须确保唯一
entry: 'http://localhost:8081/', // 子应用的入口地址,就是你子应用运行起来的地址。微应用的入口
container: '#app', // 挂载子应用内容的dom节点 `# + dom id`【见上面app.vue】。微应用的容器节点的选择器或者 Element 实例
activeRule: '/app1', // 子应用的路由前缀。当配置为字符串时会直接跟 url 中的路径部分做前缀匹配,匹配成功表明当前应用会被激活。具体见官网api
loader: (status) => { // loading 状态发生变化时会调用的方法。
console.log('loader:' + status)
},
props: { // 初始化时需要传递给微应用的数据
name: 'hehe,woshiAPP1',
}
}
]
export default apps
2、根目录src下新建文件 microapplication/index.js
//注册微应用的基础配置信息。当浏览器 url 发生变化时,会自动检查每一个微应用注册的 activeRule 规则,符合规则的应用将会被自动激活。
import { registerMicroApps, start, addGlobalUncaughtErrorHandler } from 'qiankun'
// 微应用的信息
import apps from './app'
registerMicroApps(
apps,
{
beforeLoad: (app) => {
console.log('before load', app.name)
},
beforeMount: (app) => {
console.log('before mount ', app.name)
},
afterMount: (app) => {
console.log('after mount ', app.name)
// 存储这个是为了测试,微服务之间存储内容是否通用
sessionStorage.setItem('currentApp', app.name)
},
beforeUnmount : (app) => {
console.log('before unmount ', app.name)
},
afterUnmount: (app) => {
sessionStorage.setItem('currentApp', undefined)
console.log('after unmount ', app.name)
}
},
)
// 添加全局的未捕获异常处理器
addGlobalUncaughtErrorHandler((event) => {
console.log(event)
})
export default start
3、主应用的路由
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const routers = new Router ({
mode: 'history', // 此处只能够使用history路由,不然,无法跳转到微应用界面。history发布到服务器端必须配合后端的设置
base: '/',
routes: [
{
path: '/',
component: () => import('@/pages/index.vue'),
children: [
{
path: '/',
component: () => import('@/pages/home.vue'),
children: [
{
path: '/',
component: () => import('@/pages/main.vue'),
},
{
path: '/menu1',
component: () => import('@/pages/menu1.vue')
},
{
path: '/menu2',
component: () => import('@/pages/menu2.vue')
}
]
},
{
path: '/app1',
name: 'app1'
},
{
path: '/app2',
name: 'app2',
},
{
path: '/app3',
name: 'app3',
}
]
}
]
})
export default routers
4、主应用main.js的设置
import Vue from 'vue'
import App from './App.vue'
import router from './routers'
import start from './microapplication'
import store from './store'
Vue.config.productionTip = false
start()
new Vue({
router,
store,
render: h => h(App),
}).$mount('#mainApp')
二、微应用
微应用分为有 webpack 构建和无 webpack 构建项目,有 webpack 的微应用(主要是指 Vue、React、Angular),我们验证有webpack的情况:
1、创建微应用项目 app1
2、src根目录下新增 public-path.js 文件,用于修改运行时的 publicPath。
console.log(window, window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__, window.__POWERED_BY_QIANKUN__)
if(window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
3、修改微应用main.js
import Vue from 'vue'
import App from './App.vue'
import router from './routers'
import "./public-path.js"
Vue.config.productionTip = false
// 定义一个Vue实例
let instance = null
// 渲染方法
function render(props = {}) {
const { container } = props
instance = new Vue({
router,
render: (h) => h(App)
}).$mount(container ? container.querySelector('#app'): '#app')
}
// 独立运行时
if(!window.__POWERED_BY_QIANKUN__) {
render()
}
//暴露主应用生命周期钩子
/**
* bootstrap : 在微应用初始化的时候调用一次,之后的生命周期里不再调用
*/
export async function bootstrap() {
console.log('app1 bootstraped')
}
/**
* mount : 在应用每次进入时调用
*/
export async function mount(props) {
console.log('app1 mount', props)
render(props)
}
/**
* unmount :应用每次 切出/卸载 均会调用
*/
export async function unmount() {
console.log("app1 unmount")
instance.$destroy()
instance.$el.innerHTML = ''
instance = null
}
4、微应用的路由
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const routers = new Router ({
// mode: 'history', // 此处用history路由,点击切换之后将会跳转到主应用的路由。但是,使用hash的路由不会跳转到主应用。但是,官网所显示的是推荐使用history
base: window.__POWERED_BY_QIANKUN__ ? "/app1" : "/",
routes: [
{
path: '/',
component: () => import('@/pages/index.vue'),
children: [
{
path: '/',
component: () => import('@/pages/home.vue'),
},
{
path: '/weiMenu1',
component: () => import('@/pages/menu1.vue')
},
{
path: '/weiMenu2',
component: () => import('@/pages/menu2.vue')
}
]
}
]
})
export default routers
5、微应用打包配置修改(vue.config.js)
const { name } = require('./package')
// const {__webpack_public_path__} = window
// console.log(process)
module.exports = {
publicPath: process.env.NODE_ENV === "production" ? "./" : "/",
outputDir: `dist/app1`,
parallel: false,
devServer: {
port: 8081,
headers: {
'Access-Control-Allow-Origin': '*'
}
},
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd', // 把微应用打包成 umd 库格式
jsonpFunction: `webpackJsonp_${name}`,
},
},
}
总结:这只是写了一个小小demo,如果,投入正式的项目开发中,估计,还是有很多坑需要我们踩。希望以后有机会能够让我有机会运用微应用在真实的项目开发中。
官网地址:https://qiankun.umijs.org/zh
qiankun传参讲解:https://blog.csdn.net/Lyrelion/article/details/123735057
qiankun的demo代码:https://gitee.com/kk168/qiankun.git