常见问题

1、我需要用到微前端吗?

在此之前建议你先阅读Why Not Iframe在新窗口打开

相比于iframe,微前端拥有更好的用户体验,同时它也要求开发者对于前端框架和路由原理具有一定的理解。

微前端的本质是将两个不相关的页面强行合并为一,这其中不可避免会出现各种冲突,虽然微前端框架解决了几乎所有的冲突,但偶尔也会有特殊情况出现,这需要开发者具有处理特殊情况的能力和心态。

微前端不是万能的,它的实现原理注定无法像iframe一样简单稳定。

如果你不知道自己是否需要用微前端,那么大概率是不需要。

2、子应用一定要支持跨域吗?

是的!

如果是开发环境,可以在webpack-dev-server中设置headers支持跨域。

devServer: {
  headers: {
    'Access-Control-Allow-Origin': '*',
  },
},

如果是线上环境,可以通过配置nginx在新窗口打开支持跨域。

3、兼容性如何

micro-app依赖于CustomElements和Proxy两个较新的API。

对于不支持CustomElements的浏览器,可以通过引入polyfill进行兼容,详情可参考:webcomponents/polyfills在新窗口打开

但是Proxy暂时没有做兼容,所以对于不支持Proxy的浏览器无法运行micro-app。

浏览器兼容性可以查看:Can I Use在新窗口打开

总体如下:

  • PC端:除了IE浏览器,其它浏览器基本兼容。
  • 移动端:ios10+、android5+

4、微应用无法渲染但没有报错

请检查路由配置是否正确,详情查看路由一章,或者下面第5条:jsonpFunction是否冲突

5、webpack-jsonpfunction-冲突导致渲染失败

这种情况常见于多个应用都是通过create-react-app等类似脚手架创建的项目,或一个应用多次重复渲染。

因为相同的jsonpFunction名称会导致资源加载混乱。

解决方式:修改子应用的webpack配置

// webpack.config.js
module.exports = {
  output: {
    ...
    jsonpFunction: `webpackJsonp_custom_app_name`,
    globalObject: 'window',
  },
}
// webpack.config.js
module.exports = {
  output: {
    ...
    chunkLoadingGlobal: 'webpackJsonp_custom_app_name',
    globalObject: 'window',
  },
}

6、开发时每次保存文件时报错 (热更新导致报错)

在一些场景下,热更新会导致保存时报错,请关闭热更新来解决这个问题,同时我们也在尝试更好的解决方案。

7、vue3的问题

1、样式失效

通过禁用样式隔离解决。

2、图片等静态资源无法正常加载

vue3中需要配置publicPath补全资源路径,详情请查看publicPath

8、开发环境中渲染angular子应用报错

目前需要关闭angular的热更新来解决这个问题,同时我们也在尝试更好的解决方案。

"scripts": {
  "start": "ng serve --live-reload false",
},

9、micro-app 报错 an app named xx already exists

这是name名称冲突导致的,请确保每个子应用的name值是唯一的。

10、基座应用的样式影响到子应用

虽然我们将子应用的样式进行隔离,但基座应用的样式依然会影响到子应用,如果发生冲突,推荐通过约定前缀或CSS Modules方式解决。

如果你使用的是ant-design等组件库,一般会提供添加前缀进行样式隔离的功能。

11、子应用在沙箱环境中如何获取到外部真实window?

目前有3种方式在子应用中获取外部真实window

  • 1、new Function("return window")() 或 Function("return window")()
  • 2、(0, eval)('window')
  • 3、window.rawWindow

12、错误信息:xxx 未定义

包括:

  • xxx is not defined
  • xxx is not a function
  • Cannot read properties of undefined

原因:

在微前端的沙箱环境中,顶层变量不会泄漏为全局变量。

例如在正常情况下,通过 var name 或 function name () {} 定义的顶层变量会泄漏为全局变量,通过window.name或name就可以全局访问。

但是在沙箱环境下这些顶层变量无法泄漏为全局变量,window.name或name为undefined,导致出现问题。

解决方式

方式一:手动修改

将 var name 或 function name () {} 修改为 window.name = xx

方式二:通过插件系统修改子应用代码

比如常见的加载webpack打包的dll文件失败的问题,因为dll文件的内容和js地址相对固定,可以直接进行全局查找和修改。

microApp.start({
  plugins: {
    modules: {
      应用名称: [{
        loader(code, url) {
          if (url === 'xxx.js') {
            code = code.replace('var xx_dll=', 'window.xx_dll=')
          }
          return code
        }
      }]
    }
  }
})

13、子应用加载sockjs-node失败

这个问题常见于create-react-app创建的子应用,推荐通过插件系统来解决。

microApp.start({
  plugins: {
    modules: {
      '子应用name': [{
        loader(code) {
          if (code.indexOf('sockjs-node') > -1) {
            code = code.replace('window.location.port', '子应用端口').replace('window.location.hostname', '子应用host,如果和基座相同则不需要替换hostname')
          }
          return code
        }
      }],
    }
  }
})

实际情况可能更加复杂,上面只是一种解决思路。

14、子应用请求接口失败

  • 1、请确保接口请求没有跨域问题,因为子应用被加载到基座渲染,所以请求接口是从基座发送。

  • 2、请求的接口为相对地址,会以基座域名进行补全,导致报错。

    如:fetch('/api/data'),在请求时会自动被浏览器补全为fetch(基座域名 + '/api/data')

    为了避免这个问题,子应用需要使用完整的地址:fetch(子应用域名 + '/api/data')

15、子应用反向代理失败

解决方式: 子应用使用完整的地址发送请求

如:fetch('/api/data') 改为 fetch(子应用域名 + '/api/data')

如果还是报跨域问题,则是服务端做了限制,此时需要撤除上述操作,并将子应用的代理放到基座应用中。

16、子应用多次渲染后内存越来越大

参考内存优化一章

17、子应用之间如何跳转

参考应用之间如何跳转一章

18、jsonp请求如何处理?

参考ignore

19、子应用通过a标签下载文件失败

原因: 当跨域时(基座和文件在不同域名下),无法通过a标签的download属性实现下载。

解决方式:

方式1: 转换为blob形式下载

<a href='xxx.png' download="filename.png" @click='downloadFile'>下载</a>
// 通过blob下载文件
function downloadFile (e) {
  // 微前端环境下转换为blob下载,子应用单独运行时依然使用a标签下载
  if (window.__MICRO_APP_ENVIRONMENT__) {
    e.preventDefault()
    // 注意href必须是绝对地址
    fetch(e.target.href).then((res) => {
      res.blob().then((blob) => {
        const blobUrl = window.URL.createObjectURL(blob)
        // 转化为blobURL后再通过a标签下载
        const a = document.createElement('a')
        a.href = blobUrl
        a.download = 'filename.png'
        a.click()
        window.URL.revokeObjectURL(blobUrl)
      })
    })
  }
}

方式2: 将文件放到基座域名下,判断微前端环境下a标签href属性设置为基座的文件地址