基于qiankun的微前端踩坑笔记

2,844 阅读4分钟

一、背景

刚好有一个历史比较悠久的项目,也比较奇葩,门户是由三个前端项目组成的,所以在跳转页面的时候就相对比较慢。我思来想去,觉得微前端可以试试,便开始了我的qiankun之旅,虽然最终没有落地,但是其中的坎坷,我觉得是有必要记录一下的。 前端项目:2个vue2 + 1个vue3, 都是hash路由。

二、基本操作

1、主应用配置

按照乾坤的文档配置,首先配置主应用

const apps = [
   {
     name: 'v1', 
     entry: process.env.VUE_APP_ENTRY_V1,
     container: '#subApp',
     activeRule: '/v1', 
   },
   {
     name: 'v2',
     entry: process.env.VUE_APP_ENTRY_V2,
     container: '#subApp',
     activeRule: '/v2', 
   },
   {
     name: 'v3',
     entry: process.env.VUE_APP_EENTRY_V3,
     container: '#subApp',
     activeRule: '/v3', 
   },
 ]

 registerMicroApps(apps);

 runAfterFirstMounted(() => {
   console.log("[MainApp] first app mounted");
 });
 start(
   { 
     // urlRerouteOnly: true,
     // singular: false,
     excludeAssetFilter: (assetUrl: string): boolean => {}
   }
 );

PS: enrty字段可以根据自身的环境来,

  • 如果是开发环境,可以直接写上本地启动的服务,【如://localhost:8080】
  • 如果是测试或者线上环境:要区分是同服务部署还是不同服务器部署,我这里是在同一个服务器中的【如:/xx_xx/】
  • 参考地址: qiankun.umijs.org/zh/cookbook…

2、微应用

微应用由于都是hash路由,所以在路由方面简单了很多,具体histroy路由怎么配置,可以参照文档:qiankun.umijs.org/zh/cookbook…

1)main.js

import './public-path';
Vue.use(VueLazyload, {
   loading: window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ ? `${window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__}/static/loading.png`: `/static/loading.png`
 });
function render(props = {}) {
 const { container } = props;

 instance = new Vue({
 		router,
 		store,
 		render: (h) => h(App),
         }).$mount(container ? container.querySelector('#app_mine') : '#app_mine');
 }

 // 独立运行时
 if (!window.__POWERED_BY_QIANKUN__) {
   const pvPromise = new LazyLoader('https://xxxx.xxxx.com/xx.php?id=1234&;web_id=2345').load()
   const zStatPromise = new LazyLoader('https://xx.xxx.com/xxx?ie=utf-8').load()
   render();
 }
 /**
  * bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
  * 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
  */
 export async function bootstrap() {
         console.log('我的页面bootstraped');
 }

 /**
  * 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
  */
 export async function mount(props) {
         console.log('我的页面 app mount');
         render(props);
 }

 /**
  * 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
  */
 export async function unmount(props) {
         console.log('我的页面  from main framework', props);
         instance.$destroy();
         instance.$el.innerHTML = '';
         instance = null;
         _router = null;
 }

 /**
  * 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
  */
 export async function update(props) {
 }

2)添加public-path.js

if (window.__POWERED_BY_QIANKUN__) {
   __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
 }

3) webpack配置

   output: {
       library: `${packageName}-[name]`,
       libraryTarget: 'umd',
       jsonpFunction: `webpackJsonp_${packageName}`,
   }

三、问题堆

qiankun的目的是帮助大家能更简单、无痛的构建一个生产可用微前端架构系统,但是对于刚开始接入的人来说,不走一些弯路是不可能的。

1、子应用引入第三方资源,报跨域错误

1) 可以将第三方脚本拖入本地,有自己服务器server支持跨域

2) 如果引入资源后为全局的变量,可以考虑在主应用中引入资源,微应用异步获取资源

  • 主应用
    <script type="text/javascript" src="https://xxx.xxx.com/xxx.php?xxx=xxx&;xxx=xxx"></script>
    <script src="https://xx.xx.com/xxx?ie=utf-8"></script>
    <script type="text/javascript" src="https://xxx.xxx.xxx.xxx/xxxx.js"></script>
    

3)excludeAssetFilter: 官方给出的一个方法,但是我这边尝试了,没有生效

  • 微应用
 // 独立运行时, 因为这个也不算是一个静态资源文件,所以没有办法直接放到本地,而是通过JSONP去获取的数据
    if (!window.__POWERED_BY_QIANKUN__) {
      const pvPromise = new LazyLoader('https://xxxx.xxxx.com/xx.php?id=1234&;web_id=2345').load()
      const zStatPromise = new LazyLoader('https://xx.xxx.com/xxx?ie=utf-8').load()
      render();
    }

2、Uncaught Error: application 'mine-index' died in status LOADING_SOURCE_CODE: [qiankun]: Target container with #app_mine not existed while mine-index_1 loading!

看官方文档,我排查了很久,才发现是container写错了,主应用中要留一块subApp的container给子应用,这属于常识错误。

4、主应用中配置activeRule 为'/mine-index'进入死循环

配置出错了,重新对着文档配置了一遍,就没有死循环了,我微应用和主应用配置都配置了webpack选项,估计是当初看文档的时候老严昏花了吧。

5、出现了两行dom

低级错误,微应用中渲染了两次,即new了两次实例

6、app-errors.js?17fe:11 Uncaught Error: application 'mine-index' died in status NOT_MOUNTED: [qiankun]: Target container with #subApp not existed while mine-index mounting!

这个是在vue3项目中出现的,原因是我同时创建了多个实例,第二次访问项目的时候,又创建了一次

7、静态资源404的问题

  • 1) 用网上的方法奖publicPath 设置成 localhost:${port}: 不行, 原始项目报错 vconsole.min.js?2f0e:10 Error: Loading chunk 0 failed.
  • 2) 后来自己琢磨,再看看文档
// 在懒加载资源上添加如上路径,就不报错了
Vue.use(VueLazyload, {
      loading: window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ ? `${window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__}/static/loading.png`: `/static/loading.png`
    });

四、感悟

不是所有项目都要落地微前端的,除非你的应用已经变成了一个巨石应用,维护非常难,那么集成微前端才是最好的选择。虽然我的集成微前端这个方案最终没有落地,但是也不妨碍我轻触qiankun一次。