微前端使用常见问题小结

156 阅读6分钟

腾讯无界

无界集成中可能出现的问题总结主要包括:

  1. 跨域问题:当无界微应用尝试访问 Next.js 项目的 API 时,由于安全策略(同源策略),可能会遇到跨域错误。解决办法是在 Next.js 的 next.config.js 文件中配置 headers,允许所有来源的请求。
  2. window.parent问题:在子应用中使用 iframe 时,窗口对象可能被代理,导致获取不到真正的子应用上下文。解决此问题需正确处理 window.parent 对象的引用。
  3. message显示问题:某些情况下,如在 AntdV 等库中,关闭并重新打开子应用后,message 提示可能不显示。这可能是由于子应用生命周期管理的影响,应避免过度修改子应用的生命周期。
  4. 图片路径问题:动态插入的相对 URL 图片可能不会自动转换成绝对路径,这时需要手动处理图片资源的公共路径。
  5. 窗口对象获取:在某些模式下,子应用的 window 对象是一个代理对象,需要使用特定方法(如 window.WUJIE_RAW_WINDOW)获取真实对象。
  6. 路由管理异常:特别是涉及路由切换时,如果使用了iframe,可能会影响浏览器的前进和后退操作,因为每个iframe都有独立的路由栈。
  7. 官网解释的常见问题
1、请求资源报错
2、第三方包已经引入,使用时报错
3、子应用的字体没有生效
4、冒泡系列组件(比如下拉框)弹出位置不正确
5、子应用处理异步处理事件时,e.target 变成了 wujie-app
6、css 样式内部的相对地址相对的是主应用的域名
7、子应用使用 module federation 引用远程模块报错
8、子应用 iframe 初始化时加载、执行了主应用的资源
9、子应用 window 是一个代理对象,如何获取子应用的真实对象
10、DOMException: Blocked a frame with origin from accessing a cross-origin frame 报错
11、子应用的相对地址图片没有替换成绝对地址
12、vite4 子应用样式切换丢失

阿里乾坤

主子应用样式相互影响

各个应用样式隔离 这个问题乾坤框架做了一定的处理,在运行时有一个sandbox的参数,默认情况下沙箱可以确保单实例场景子应用之间的样式隔离,但是无法确保主应用跟子应用、或者多实例场景的子应用样式隔离。如果要解决主应用和子应用的样式问题,目前有2种方式:

  • 在乾坤种配置 { strictStyleIsolation: true } 时表示开启严格的样式隔离模式。这种模式下 qiankun 会为每个微应用的容器包裹上一个 shadow dom 节点,从而确保微应用的样式不会对全局造成影响。但是基于 ShadowDOM 的严格样式隔离并不是一个可以无脑使用的方案,大部分情况下都需要接入应用做一些适配后才能正常在 ShadowDOM 中运行起来,这个在 qiankun 的 issue 里面有一些讨论和使用经验。
  • 人为用 css 前缀来隔离开主应用和子应用,在组件层面用 css scoped进行组件层面的样式区分,在 css框架层面可以给css组件库加上不同的前缀,比如文档中的 antd 例子:配置 webpack 修改 less 变量

代码语言:javascript

{
  loader: 'less-loader',
+ options: {
+   modifyVars: {
+     '@ant-prefix': 'yourPrefix',
+   },
+   javascriptEnabled: true,
+ },
}

b. 配置 antd ConfigProvider

import { ConfigProvider } from 'antd';
   
export const MyApp = () => (
  <ConfigProvider prefixCls="yourPrefix">
    <App />
  </ConfigProvider>
);

应用间通信

  1. localStorage/sessionStorage
  2. 通过路由参数共享
  3. 官方提供的 props
  4. 官方提供的 actions
  5. 使用vuex或redux管理状态,通过shared分享

具体实现参考这篇文章 qiankun的五种通信方式

qiankun 实现 keep-alive 需求

子项目 keep-alive 其实就是想在子应用切换时不卸载掉,仅仅是样式上的隐藏(display: none),这样下次打开就会更快。

但是 keep-alive 需要谨慎使用,同时加载并运行多个子应用,这将会增加 js/css 污染的风险。

产品那边其实当时是有提出这个需求的,当时第一时间想到的是借助 qiankun 的 loadMicroApp 函数来手动加载和卸载子应用。但是公司的项目主应用嵌入了十几个子应用,想到需要一个个处理,以及手动加载和卸载子应用所可能带来的一些边界问题处理,后面直接说这个需求不好实现。之后也就暂时搁置了

具体解决方案可以看 qiankun issues 里所给出的

路由跳转问题

在子应用中是没有办法通过 <router-link> 或者用 router.push/router.replace 直接跳转的,因为这个 router 是子应用的路由,所有的跳转都会基于子应用的 base 。当然了写 <a> 链接可以跳转过去,但是会刷新页面,用户体验并不好。

这里可以采用以下两种方式:

  • 将主应用的路由实例通过 props 传给子应用,子应用用这个路由实例跳转。
  • 路由模式为 history 模式时,通过 history.pushState() 方式跳转

这里我把他封装为了一个常用方法

代码语言:javascript

/**
 * 微前端子应用路由跳转
 * @param {String} url 路由
 * @param {Object} mainRouter 主应用路由实例
 * @param {*} params 状态对象:传给目标路由的信息,可为空
 */

const qiankunJump = (url, mainRouter, params) => {
  if (mainRouter) {
    // 使用主应用路由实例跳转
    mainRouter.push({ path: url, query: params })
    return
  }
  // 未传递主应用路由实例,传统方式跳转
  let searchParams = '?'
  let targetUrl = url
  if (typeOf(params) === 'object' && Object.keys(params).length) {
    Object.keys(params).forEach(item => {
      searchParams += `${item}=${params[item]}&`
    })
    targetUrl = targetUrl + searchParams.slice(0, searchParams.length - 1)
  }
  window.history.pushState(null, '', targetUrl)
}

适配vue-pdf 报错

找到vue-pdf的依赖包下的vuePdfNoSss.vue

代码语言:javascript

//找到vue-pdf的依赖包下的vuePdfNoSss.vue
<style src="./annotationLayer.css"></style>
<script>
	import componentFactory from './componentFactory.js'
	if ( process.env.VUE_ENV !== 'server' ) {
		var pdfjsWrapper = require('./pdfjsWrapper.js').default;
		var PDFJS = require('pdfjs-dist/es5/build/pdf.js');
		if ( typeof window !== 'undefined' && 'Worker' in window && navigator.appVersion.indexOf('MSIE 10') === -1 ) {
      // 注释原本的引入方法
			// var PdfjsWorker = require('worker-loader!pdfjs-dist/es5/build/pdf.worker.js');
			  var PdfjsWorker=require('pdfjs-dist/es5/build/pdf.worker.js');
			PDFJS.GlobalWorkerOptions.workerPort = new PdfjsWorker();
		}
		var component = componentFactory(pdfjsWrapper(PDFJS));
	} else {
		var component = componentFactory({});
	}
	export default component;
</script>

修改项目的配置文件vue.config.js

chainWebpack: (config) => {
  config.module
    .rule('worker')
    .test(/.worker.js$/)
    .use('worker-loader').loader('worker-loader')
    .options({
      inline: true,
      fallback: false
    }).end();
}

主项目和子项目部署到一起,子项目部署到二级目录(不占用这么多端口)

因为客户方的要求,可能有时候不允许服务器开太多的端口,因此需要把主应用和微应用部署到一起,公用一个端口。

主项目和子项目部署到一起,子项目部署到二级目录

qiankun在子应用中引入百度地图时报错解决

因为qiankun会把静态资源的加载拦截,改用fetch方式获取资源,所以要求这些资源支持跨域,这里我们使用qiankun提供的 excludeAssetFilter 将其加入白名单放行。

  • excludeAssetFilter - (assetUrl: string) => boolean - 可选,指定部分特殊的动态加载的微应用资源(css/js) 不被 qiankun 劫持处理

修改主应用 start 方法

代码语言:javascript

// 启动微前端
if (!window.qiankunStarted) {
  window.qiankunStarted = true
  start({
    singular: false,
    excludeAssetFilter: (assetUrl) => {
      // 过滤baidu
      const wihiteWords = ['baidu']
      if (wihiteWords.includes(assetUrl)) {
        return true
      }
      return wihiteWords.some(w => {
        return assetUrl.includes(w)
      })
    }
  })
}

其他一些常见问题可见于 qiankun官网

总的来说,微前端确实解决了一些项目中的痛点,但是切记微前端不是银弹,老旧项目带来的迁移成本,不同项目技术栈的兼容与边界问题处理,因为没有迫切的需求而接入微前端,只会带来额外的负担,很多时候,iframe 其实就很够用了。

参考链接:blog.csdn.net/BradenHan/a… cloud.tencent.com/developer/a…