qiankun框架的使用
1、主应用
$ yarn add qiankun # 或者 npm i qiankun -S
1.1 在主应用中注册微应用
import { registerMicroApps } from 'qiankun'
const subArr=[
{
name: 'react app', // app name registered
entry: '//localhost:7100',
container: '#yourContainer',
activeRule: '/yourActiveRule',
},
{
name: 'vue app',
entry: { scripts: ['//localhost:7100/main.js'] },
container: '#yourContainer2',
activeRule: '/yourActiveRule2',
},
{
name: 'financial',
entry: '//localhost:8848/financial-center',
container: '#yourContainer2',
activeRule: '/financial'.replace(//(\w+)/, '/m-$1')
},
]
export function registerApps() {
window.jQuery = undefined
try {
registerMicroApps(subArr, {
beforeLoad: [
app => {}
],
beforeMount: [
app => {}
],
afterMount: [
app => {}
],
beforeUnmount: [
app => {}
],
afterUnmount: [
app => {}
]
})
} catch (err) {
console.log('registerApps::', err)
}
}
说明: ~~~~~ ~~~~~ ~~~~~ ~~~~~ ~~~~
subArr: 表示关于子应用的路由配置:
-
name: 必选,微应用的名称,微应用之间必须确保唯一; -
entry: -string | { scripts?: string[]; styles?: string[]; html?: string }- 必选,微应用的入口。- 配置为字符串时,表示微应用的访问地址,例如
https://qiankun.umijs.org/guide/。
- 配置为字符串时,表示微应用的访问地址,例如
-
配置为对象时,
html的值是微应用的 html 内容字符串,而不是微应用的访问地址。微应用的publicPath将会被设置为/。 -
container-string | HTMLElement- 必选,微应用的容器节点的选择器或者 Element 实例。 -
activeRule-string | (location: Location) => boolean | Array boolean>- 必选,微应用的激活规则。-
支持直接配置字符串或字符串数组,如
activeRule: '/app1'或activeRule: ['/app1', '/app2'],当配置为字符串时会直接跟 url 中的路径部分做前缀匹配,匹配成功表明当前应用会被激活。 -
支持配置一个 active function 函数或一组 active function。函数会传入当前 location 作为参数,函数返回 true 时表明当前微应用会被激活。如
location => location.pathname.startsWith('/app1')注意1:微应用建议使用
history模式的路由,需要设置路由base,值和它的activeRule是一样的。注意2:而且微应用的路由地址和主应用
activeRule规则返回的一定不要一样,防止刷新主应用只展示微应用的内容。注意3:当浏览器
url发生变化时,会自动检查每一个微应用注册的activeRule规则,符合规则的应用将会被自动激活。
-
主应用声明周期:
beforeLoad-Lifecycle | Array- 可选beforeMount-Lifecycle | Array- 可选afterMount-Lifecycle | Array- 可选beforeUnmount-Lifecycle | Array- 可选afterUnmount-Lifecycle | Array- 可选
1.2 主应用的启动在app.vue文件中
import {
start,
initGlobalState,
addGlobalUncaughtErrorHandler,
removeGlobalUncaughtErrorHandler
} from 'qiankun'
export default {
methods: {
handleError(event) {
console.error('qiankun error ->', event)
if (
(event.error && event?.error?.stack) ||
(event.reason && event.reason?.message)
) {
const errmsg = event?.error?.stack || event?.reason?.message
if (
(errmsg.includes("Unexpected token '<'") &&
(errmsg.includes('evalCode') || errmsg.includes('eval'))) ||
errmsg.includes('fetch dynamically imported module')
) {
console.log("[qiankun] ignore fetch error");
}
}
}
},
created() {
const { onGlobalStateChange,setGlobalState } = initGlobalState(
this.qiankunState
)
// 主应用监听变化 主应用可以通过setGlobalState(value)的方法 可以在onGlobalStateChange监控到
onGlobalStateChange((value, prev) => {
console.info('[onGlobalStateChange - master]:', value, prev)
})
},
mounted() {
// console.log('loadingIsShow111', this.loadingIsShow)
if (!window.qiankunStarted) {
window.qiankunStarted = true
registerApps() // 上述方法请自行引入
start({
prefetch: false, // 预加载
sandbox: {
experimentalStyleIsolation: false // 样式隔离
},
excludeAssetFilter: assetUrl => {
// 指定部分特殊的动态加载的微应用资源(css/js) 不被 qiankun 劫持处理
// 自定义白名单链接
const whiteList = [
'javascripts/jquery-1.7.2.min.js',
]
if (whiteList.find(item => assetUrl.indexOf(item) > -1)) {
console.log('output->assetUrl', assetUrl)
return true
}
}
})
// 添加全局的异常处理
addGlobalUncaughtErrorHandler(this.handleError)
}
},
beforeDestroy() {
removeGlobalUncaughtErrorHandler(this.handleError)
}
}
1.3主应的监控和子应用的传递更改
官方案例:
主应用:
import { initGlobalState, MicroAppStateActions } from 'qiankun';
// 初始化 state
const actions: MicroAppStateActions = initGlobalState(state);
actions.onGlobalStateChange((state, prev) => { // state: 变更后的状态; prev 变更前的状态 console.log(state, prev);
});
actions.setGlobalState(state);actions.offGlobalStateChange();
微应用:
// 从生命周期 mount 中获取通信方法,使用方式和 master 一致
export function mount(props) {
props.onGlobalStateChange((state, prev) => { // state: 变更后的状态; prev 变更前的状态 console.log(state, prev);
});
props.setGlobalState(state);}
2、微应用
2.1、微应用以webpack vue2.x版本(不用再单独安装乾坤框架)main.js中
let instance = null
// 保存原有document.body.appendChild方法
let originFn = document.body.appendChild.bind(document.body)
function render(props = {}) {
const { container } = props
if (container) {
// 重写appendChild方法
document.body.appendChild = dom => {
// 根据标记,来区分是否用新的挂载方式
if (dom?.className?.indexOf('goods-') > -1) {
container.querySelector('#goods-center').appendChild(dom)
} else {
originFn(dom)
}
}
}
!router && createRouter()
instance = new Vue({
router,
store,
render: h => h(App)
}).$mount(
container ? container.querySelector('#app') : '#app'
)
}
// 区分是否是qiankun环境
if (!window.__POWERED_BY_QIANKUN__) {
render()
}
export async function bootstrap() {
console.log('app bootstraped')
}
export async function mount(props) {
console.log('props from main framework', props)
render(props)
}
export async function unmount() {
instance.$destroy()
instance.$el.innerHTML = ''
instance = null
document.body.appendChild = originFn
}
区分qiankun环境:window.POWERED_BY_QIANKUN
微应用生命周期:
bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
2.2 vue3+ vite微应用 main.js
先下载依赖
pnpm install vite-plugin-qiankun
import App from './App.vue'
import {
qiankunWindow,
renderWithQiankun,
} from 'vite-plugin-qiankun/dist/helper'
let app = null
const render = async props => {
app = createApp(App)
const { container } = props
app.mount(
container instanceof Element
? container.querySelector('#markting')
: document.getElementById('markting'),
)
}
let qiankunProps = null
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
render({})
} else {
renderWithQiankun({
async mount(props) {
await render(props)
props.onGlobalStateChange((state, prev) => {
// state: 变更后的状态; prev 变更前的状态
console.log(state, prev)
})
qiankunProps = props
},
bootstrap() {
console.log('--bootstrap')
},
update() {
console.log('--update')
},
unmount() {
if (!app) return
app.unmount()
if (app?._container?.innerHTML) {
app._container.innerHTML = ''
}
if (app?.$el?.innerHTML) {
app.$el.innerHTML = ''
}
app = null
},
})
}
// 监听
export function qiankunEmit(state) {
state && qiankunProps?.setGlobalState(state)
}
注意:当微应用信息注册完之后,一旦浏览器的 url 发生变化,便会自动触发 qiankun 的匹配逻辑,所有 activeRule 规则匹配上的微应用就会被插入到指定的 container 中,同时依次调用微应用暴露出的生命周期钩子。
微应用分为有 webpack 构建和无 webpack 构建项目,有 webpack 的微应用(主要是指 Vue、React、Angular)需要做的事情有:
-
新增
public-path.js文件,用于修改运行时的publicPath。注意:运行时的
publicPath和构建时的publicPath是不同的,两者不能等价替代。
在 src 目录新增 public-path.js:
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
- 微应用建议使用
history模式的路由,需要设置路由base,值和它的activeRule是一样的。 - 在入口文件最顶部引入
public-path.js,修改并导出三个生命周期函数。 - 修改
webpack打包,允许开发环境跨域和umd打包。
主要的修改就是以上四个,可能会根据项目的不同情况而改变。例如,你的项目是 index.html 和其他的所有文件分开部署的,说明你们已经将构建时的 publicPath 设置为了完整路径,则不用修改运行时的 publicPath (第一步操作可省)。
无 webpack 构建的微应用直接将 lifecycles 挂载到 window 上即可(vite 可以使用上面得插件)。