微前端框架 (qiankun) 应用记录
参考:
- https://qiankun.umijs.org/zh/…
- https://blog.csdn.net/hjb2722…
- 其余
纳闷
- 主框架和子框架是否都要装置乾坤框架
答:不是,只须要装置在主框架即可,子框架不须要引入
应用办法
主利用
- 路由倡议应用 history 模式
-
装置 qiankun
yarn add qiankun
或者npm i qiankun -S
-
配置微利用
// /src/micro/apps.js const 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.js import {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.js import 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' // 加载乾坤 app microApp // 注册利用 // 蕴含子利用容器的 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.js const 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.js if (window.__POWERED_BY_QIANKUN__) { // eslint-disable-next-line no-undef __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; } // /src/main.js import '@/micro/public-path'
-
路由实例化配置
// src/router/index.js import 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.js import "@/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-ignore if (!window.__POWERED_BY_QIANKUN__) { // 默认独立运行 render();} // 父利用加载子利用,子利用必须裸露三个接口:bootstrap、mount、unmount export 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.js
import {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.js
function 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.js
import "@/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-ignore
if (!window.__POWERED_BY_QIANKUN__) {
// 默认独立运行
render();}
// 父利用加载子利用,子利用必须裸露三个接口:bootstrap、mount、unmount
export 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