--- nav: title: 常见问题 toc: menu --- # 常见问题 ## `Application died in status LOADING_SOURCE_CODE: You need to export the functional lifecycles in xxx entry` qiankun 抛出这个错误是因为无法从微应用的 entry js 中识别出其导出的生命周期钩子。 可以通过以下几个步骤解决这个问题: 1. 检查微应用是否已经导出相应的生命周期钩子,参考[文档](/zh/guide/getting-started#1-导出相应的生命周期钩子)。 2. 检查微应用的 webpack 是否增加了指定的配置,参考[文档](/zh/guide/getting-started#2-配置微应用的打包工具)。 3. 检查微应用的 `package.json` 中的 `name` 字段是否是微应用中唯一的。 4. 检查微应用的 entry html 中入口的 js 是不是最后一个加载的脚本。如果不是,需要移动顺序将其变成最后一个加载的 js,或者在 html 中将入口 js 手动标记为 `entry`,如: ```html {2} ``` 如果在上述步骤完成后仍有问题,通常说明是浏览器兼容性问题导致的。可以尝试 **将有问题的微应用的 webpack `output.library` 配置成跟主应用中注册的 `name` 字段一致**,如: 假如主应用配置是这样的: ```ts {4} // 主应用 registerMicroApps([ { name: 'brokenSubApp', entry: '//localhost:7100', container: '#yourContainer', activeRule: '/react', }, ]); ``` 将微应用的 `output.library` 改为跟主应用中注册的一致: ```js {4} module.exports = { output: { // 这里改成跟主应用中注册的一致 library: 'brokenSubApp', libraryTarget: 'umd', jsonpFunction: `webpackJsonp_${packageName}`, }, }; ``` ## Vue Router 报错 `Uncaught TypeError: Cannot redefine property: $router` qiankun 中的代码使用 Proxy 去代理父页面的 window,来实现的沙箱,在微应用中访问 `window.Vue` 时,会先在自己的 window 里查找有没有 `Vue` 属性,如果没有就去父应用里查找。 在 VueRouter 的代码里有这样三行代码,会在模块加载的时候就访问 `window.Vue` 这个变量,子项目中报这个错,一般是由于父应用中的 Vue 挂载到了父应用的 `window` 对象上了。 ```javascript if (inBrowser && window.Vue) { window.Vue.use(VueRouter) } ``` 可以从以下方式中选择一种来解决问题: 1. 在主应用中不使用 CDN 等 external 的方式来加载 `Vue` 框架,使用前端打包软件来加载模块 2. 在主应用中,将 `window.Vue` 变量改个名称,例如 `window.Vue2 = window.Vue; window.Vue = undefined` ## Vue 框架下使用 Vue Router 的注意点 qiankun 主应用根据 `activeRule` 配置激活对应微应用。 ### a. 主应用是 hash 模式 当主应用是 hash 模式时,一般微应用也是 hash 模式。主应用的一级 hash 路径会分配给对应的微应用(比如 `#/base1` ),此时微应用如果需要在 base 路径的基础上进行 hash 模式下的二级路径跳转(比如 `#/base1/child1` ),这个场景在当前 VueRouter 的实现方式下需要自己手动实现,给所有路由都添加一个前缀即可。VueRouter 的 hash 模式下的 base 参数[不支持添加 hash 路径 base](https://github.com/vuejs/vue-router/blob/dev/src/index.js#L55-L69)。 ### b. 主应用是 history 模式 当主应用是 history 模式且微应用也是 history 模式时,表现完美。如果微应用需要添加 base 路径,设置子项目的 [base](https://router.vuejs.org/zh/api/#base) 属性即可。 当主应用是 history 模式,微应用是 hash 模式,表现完美。 ## 为什么微应用加载的资源会 404? 原因是 webpack 加载资源时未使用正确的 `publicPath`。 可以通过以下两个方式解决这个问题: ### a. 使用 webpack 运行时 publicPath 配置 qiankun 将会在微应用 bootstrap 之前注入一个运行时的 publicPath 变量,你需要做的是在微应用的 entry js 的顶部添加如下代码: ```js __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; ``` 关于运行时 publicPath 的技术细节,可以参考 [webpack 文档](https://webpack.js.org/guides/public-path/#on-the-fly)。 runtime publicPath 主要解决的是微应用动态载入的 脚本、样式、图片 等地址不正确的问题。 ### b. 使用 webpack 静态 publicPath 配置 你需要将你的 webpack `publicPath` 配置设置成一个绝对地址的 url,比如在开发环境可能是: ```js { output: { publicPath: `//localhost:${port}`, } } ``` ## 微应用静态资源一定要支持跨域吗? 是的。 由于 qiankun 是通过 fetch 去获取微应用的引入的静态资源的,所以必须要求这些静态资源支持[跨域](https://developer.mozilla.org/zh/docs/Web/HTTP/Access_control_CORS)。 如果是自己的脚本,可以通过开发服务端跨域来支持。如果是三方脚本且无法为其添加跨域头,可以将脚本拖到本地,由自己的服务器 serve 来支持跨域。 参考:[Nginx 跨域配置](https://segmentfault.com/a/1190000012550346) ## 如何解决由于运营商动态插入的脚本加载异常导致微应用加载失败的问题 运营商插入的脚本通常会用 async 标记从而避免 block 微应用的加载,这种通常没问题,如: ```html ``` 但如果有些插入的脚本不是被标记成 async 的,这类脚本一旦运行失败,将会导致整个应用被 block 且后续的脚本也不再执行。我们可以通过以下几个方式来解决这个问题: ### 使用自定义的 getTemplate 方法 通过自己实现的 getTemplate 方法过滤微应用 HTML 模板中的异常脚本 ```js import { start } from 'qiankun'; start({ getTemplate(tpl) { return tpl.replace(' ``` 2. 在 entry js 里声明 lifecycles ```javascript const render = ($) => { $('#purehtml-container').html("Hello, render with jQuery"); return Promise.resolve(); } (global => { global['purehtml'] = { bootstrap: () => { console.log('purehtml bootstrap'); return Promise.resolve(); }, mount: () => { console.log('purehtml mount'); return render($); }, unmount: () => { console.log('purehtml unmount'); return Promise.resolve(); }, }; })(window); ``` 你也可以直接参照 examples 中 purehtml 部分的[代码](https://github.com/umijs/qiankun/tree/master/examples/purehtml) 同时,你也需要开启相关资源的 CORS,具体请参照[此处](#微应用静态资源一定要支持跨域吗?) ## 子应用 JSONP 跨域错误怎么处理? qiankun 会将子应用的动态 script 加载(例如 JSONP)转化为 fetch 请求,因此需要相应的后端服务支持跨域,否则会导致错误。 在单实例模式下,你可以使用 `excludeAssetFilter` 参数来放行这部分资源请求,但是注意,被该选项放行的资源会逃逸出沙箱,由此带来的副作用需要你自行处理。 若在多实例模式下使用 JSONP,单纯使用 `excludeAssetFilter` 并不能取得好的效果,因为各应用被沙箱所隔离;你可以在主应用提供统一的 JSONP 工具,子应用调用主应用提供的该工具来曲线救国。