微前端框架(qiankun)应用记录
参考:
- https://qiankun.umijs.org/zh/...
- https://blog.csdn.net/hjb2722...
- 其余
纳闷
- 主框架和子框架是否都要装置乾坤框架
答:不是,只须要装置在主框架即可,子框架不须要引入
应用办法
主利用
- 路由倡议应用history模式
装置 qiankun
yarn add qiankun
或者npm i qiankun -S
配置微利用
// /src/micro/apps.jsconst apps = [ { name: 'planResource', entry: '//localhost:8083', container: '#iframe', activeRule: '/plan' }, { name: 'configration', entry: '//localhost:8081', container: '#iframe', activeRule: '/configure' }];export default apps;
在主利用中注册微利用
// src/micro/index.jsimport { registerMicroApps, addGlobalUncaughtErrorHandler, start } from 'qiankun';import apps from './apps';// registerMicroApps 第二个参数能够不要,如果要做点啥,就写到对应的中央registerMicroApps(apps,{ beforeLoad: [ app => { console.log('[LifeCycle] before load %c%s', 'color: green;', app.name); }, ], beforeMount: [ app => { console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name); }, ], afterUnmount: [ app => { console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name); }, ],},);// 出错时显示的内容addGlobalUncaughtErrorHandler((event) => { // console.log(event); const { message } = event; if (message && message.includes('died in status LOADING_SOURCE_CODE')) { console.log('微利用加载失败,请查看利用是否可运行'); }});export default start;
应用 start
// /src/main.jsimport microApp from './micro';new Vue({ router, store, render: (h) => h(App)}).$mount('#app');microApp();
留神:
<font color=orange>不肯定要放在入口文件内,看你的我的项目而定</font>
- 如果你的微利用容器在index.html里的话,是没有多大问题,
然而很多时候是嵌入在Layout框架里的,失常进入是没有问题,如果是在微利用门路下刷新页面,会有如下图片的报错
<font color=red>Uncaught Error: application 'cloud' died in status LOADING_SOURCE_CODE: [qiankun] Target container with #cloud not existed while cloud loading!</font>
这种状况下倡议在改选件的mounted事件钩子下start, main.js下引入,不执行即可
eg:
//main.js...import microApp from './micro' // 加载乾坤appmicroApp // 注册利用// 蕴含子利用容器的VUE组件<template> <div class="vab-app-main"> <section> <transition mode="out-in" name="fade-transform"> <vab-keep-alive v-if="routerView" /> </transition> <div id="cloud"></div> <div id="vue3"></div> </section> <vab-footer /> </div></template><script> ... import { start } from 'qiankun' export default { name: 'VabAppMain', data() { return { ... } }, ... mounted() { start({ prefetch: false, // 勾销预加载 }) // 开启 }, ... }</script>
子利用
批改webpack配置
// webpack.config.js || vue.config.jsconst port = 8088;const packageName = require("./package.json").name;module.exports = { lintOnSave: false, // 敞开eslint检测 devServer: { port, // 这里的端口是必须和父利用配置的子利用端口统一 disableHostCheck: false, // 敞开主机查看,保障子利用能够被主利用fetch到 headers: { //因为qiankun外部申请都是fetch来申请资源,所以子利用必须容许跨域 "Access-Control-Allow-Origin": "*", }, }, configureWebpack: { output: { //资源打包门路 library: `${packageName}`, libraryTarget: "umd", jsonpFunction: `webpackJsonp_${packageName}`, // publicPath: `//localhost:${port}`, }, }, // outputDir: 'dist', // assetsDir: 'static', // filenameHashing: true,};
减少 public-path配置文件并在入口文件引入
// /src/micro/public-path.jsif (window.__POWERED_BY_QIANKUN__) { // eslint-disable-next-line no-undef __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;}// /src/main.jsimport '@/micro/public-path'
路由实例化配置
// src/router/index.jsimport Vue from 'vue'import VueRouter from 'vue-router'Vue.use(VueRouter)const routes = [ { path: '/', name: 'Home', component: () => import('../views/Home.vue') }, { path: '/about', name: 'About', component: () => import('../views/About.vue') }]const router = new VueRouter({ mode: 'history', // @ts-ignore base: window.__POWERED_BY_QIANKUN__ ? '/cloud' : '/', // 独立运行是用"/"作为根门路 // base: '/cloud', routes})export default router
Vue实例化包装和生命周期钩子导出
// /src/main.jsimport "@/micro/public-path";import Vue from "vue";import App from "./App.vue";import actions from "./micro/actions.js";import router from "./router";import store from "./sotre";let instance = null;function render(props = {}) { // 通过实际这个判断要有,不然在独立运行时报错 if (window.__POWERED_BY_QIANKUN__) { if (props) { actions.setActions(props); actions.onGlobalStateChange((state) => { console.log("app1", state); }, true); } } const { container } = props; // 这里是挂载到本人的html中 基座会拿到这个挂载后的html 将其插入进去 const renderContainer = container ? container.querySelector("#app") : "#app"; instance = new Vue({ router, store, render: (h) => h(App), }).$mount(renderContainer);}// @ts-ignoreif (!window.__POWERED_BY_QIANKUN__) { // 默认独立运行 render();}// 父利用加载子利用,子利用必须裸露三个接口:bootstrap、mount、unmountexport async function bootstrap() { console.log("cloud app bootstraped");}export async function mount(props) { console.log("mount"); render(props);}export async function unmount() { console.log("unmount"); instance.$destroy();}
传值 <font color=red>Actions</font>
props 传值不够灵便,不举荐应用
<font color=red>Actions</font>举荐应用办法,能够在状态扭转时触发
主利用
- 初始化全局跨利用通信办法
// /src/micro/actions.jsimport { initGlobalState } from 'qiankun';const initState = {};const actions = initGlobalState(initState);export default actions;
- 在须要设置跨利用全局状态的组件内:
// /src/app.vue<template> <div id="app"> <router-view /> </div></template><script> import actions from '@/micro/actions' export default { name: 'App', computed: { visitedRoutes() { return this.$store.state.tabs.visitedRoutes //须要监听的数据 }, }, watch: { visitedRoutes(newVal, oldVal) { this.setGlobalState() }, }, created() { // this.setGlobalState() }, methods: { setGlobalState() { let data = { time: this.$store.state.user.time, token: this.$store.state.user.token, ability: this.$store.state.acl, routes: this.$store.state.routes, tabs: this.$store.state.tabs.visitedRoutes, } actions.setGlobalState(data) }, }, }</script>
留神:
<font color=orange>能够监听vuex数据,如果变动,执行setGlobalState</font>
子利用
- 全局actions定义
// /src/micro/actions.jsfunction emptyAction() { // 正告:提醒以后应用的是空 Action console.warn('Current execute action is empty!'); } class Actions { // 默认值为空 Action actions = { onGlobalStateChange: emptyAction, setGlobalState: emptyAction }; /** * 设置 actions */ setActions(actions) { this.actions = actions; } /** * 映射 */ onGlobalStateChange(...args) { return this.actions.onGlobalStateChange(...args); } /** * 映射 */ setGlobalState(...args) { return this.actions.setGlobalState(...args); } } const actions = new Actions(); export default actions;
- 在利用渲染前获取从主利用上的通信办法并注入到actions里
// /src/main.jsimport "@/micro/public-path";import Vue from "vue";import App from "./App.vue";import actions from "./micro/actions.js";import router from "./router";import store from "./sotre";let instance = null;function render(props = {}) { // @ts-ignore if (window.__POWERED_BY_QIANKUN__) { if (props) { actions.setActions(props); actions.onGlobalStateChange((state) => { console.log("app1", state); const { token, ability, tabs } = state; store.state.ability = ability store.state.token = token store.state.tabs = tabs }, true); } } const { container } = props; // 这里是挂载到本人的html中 基座会拿到这个挂载后的html 将其插入进去 const renderContainer = container ? container.querySelector("#app") : "#app"; instance = new Vue({ router, store, render: (h) => h(App), }).$mount(renderContainer);}// @ts-ignoreif (!window.__POWERED_BY_QIANKUN__) { // 默认独立运行 render();}// 父利用加载子利用,子利用必须裸露三个接口:bootstrap、mount、unmountexport async function bootstrap() { console.log("cloud app bootstraped");}export async function mount(props) { console.log("mount"); render(props);}export async function unmount() { console.log("unmount"); instance.$destroy();}
开发DevTools
如果基座和子利用应用的都是VUE,要应用devtools是比拟老火的事件:
- 如果应用的devtools 5.xx 的版本,那么只能查看主框架的数据
- 如果应用devtools 6.xx(目前只有bata版本),且基座和子利用应用不同的VUE版本,那么能够查看对应利用的数据,<font color=orange>可是开发开发工具会不停的报错,蛋痛</font>
倡议:基座框架和子框架应用不同的框架语言开发(这样是否要好一些,或者有解决的方法)
<font color=red>主利用在什么时候传值</font>
很重要:不然首次是拿不到值的哦
参考传值Actions 主利用下的设置 监听vuex