qiankun 踩坑指南

鉴于前一段时间公司内部项目用到了微前端框架qiankun,总理了一些常见的坑:

指定 yarn 模块下载源

yarn config set registry https://registry.npm.taobao.org

指定 node-sass 下载源

yarn config set sass-binary-site http://npm.taobao.org/mirrors/node-sass

指定 electron 下载源

yarn config set electron_mirror https://npm.taobao.org/mirrors/electron/

指定 chromedriver 下载源

yarn config set chromedriver_cdnurl https://npm.taobao.org/mirrors/chromedriver

qiankun 常见报错
子项目未 export 需要的生命周期函数
先检查下子项目的入口文件有没有 export 生命周期函数,再检查下子项目的打包,最后看看请求到的子项目的文件对不对。

子项目加载时,容器未渲染好

检查容器 div 是否是写在了某个路由里面,路由没匹配到所有未加载。如果只在某个路由页面加载子项目,可以在页面的 mounted 周期里面注册子项目并启动。 ⚠️:在mounted注册也会有问题 主项目路由的** hash history **之争

主项目 history : 需要使用 location.pathname 来区分不同的子项目 hash 模式子项目路由跳转不改变 path,所以无影响, history 模式子项目路由设置 base 属性即可。 缺点:

  1. history 模式路由需要设置 base
  2. 子项目之间的跳转需要使用父项目的 router 对象(不用 <a> 链接直接跳转的原因是 <a> 链接会刷新页面)。

其实不传递 router 对象,用原生的 history 对象跳转也行: history.pushState(null, 'name', '/app-vue-hash/#/about'),同样不会刷新页面。

不管是父项目的 router 对象,还是原生的 history 对象,跳转都是 js 的方式。这里有一个小小的用户体验问题:标签(<router-link> 和 <a>)形式的跳转是支持浏览器默认的右键菜单的,js 方式则没有:

主项目路由用 hash 模式且子项目没有history 模式路由

也就是说主项目和所有子项目都是 hash 模式,这种情况下也有两种做法:

  1. 用 path 来区分子项目

做法就不赘述了

优点:无需修改子项目内部代码

缺点:项目之间的跳转,都得靠原生的 history 对象

  1. 用 hash 来区分子项目

这样做主项目和子项目会共同接管路由,举个栗子:

  • /#/vue/home: 会加载 vue 子项目的 home 页面,但是其实,单独访问这个子项目的 home 页面的完整路由就是/#/vue/home

  • /#/react/about: 会加载 react 子项目的 about 页面,同样,单独访问这个子项目的 about 页面的完整路由就是/#/react/about

  • /#/about: 会加载主项目的about页面

做法就是自定义 activeRule :

const getActiveRule = hash => location => location.hash.startsWith(hash);

registerMicroApps([

{

name: 'app-vue-hash',

entry: 'http://localhost:1111',

container: '#appContainer',

activeRule: getActiveRule('#/app-vue-hash'),

},

])

复制代码

然后需要在子项目的所有路由前加上这个前缀,或者将子项目的根路由设置为这个前缀。

const routes = [

{

path: '/app-vue-hash',

name: 'Home',

component: Home,

children: [

// 其他的路由都写到这里

]

}

]

复制代码

如果子项目是新项目还好,如果是旧项目,则影响还是比较大,子项目里面的路由跳转(<router-link>、router.push()、router.repace())如果使用的是 path ,则需要修改,得加上这个前缀,如果使用的是 name跳转,则无需改动:router.push({ name: 'user'})。

优点: 所有项目之间的跳转都可以直接使用自己的 router 对象或者 <router-link>,不需要借助父项目的路由对象或者原生的 history 对象

缺点: 对子项目是入侵式修改,如果是全新项目,则无影响。

主项目路由用 hash 模式且子项目有history 模式路由

主项目是hash 模式,子项目间的跳转就只能借助原生的 history 对象了,我们既可以用 path 也可以用 hash 来区分子项目:

  1. 用 path 来区分子项目

与主项目是 history 没有太大的差异,优缺点也一样。

  • /vue-hash/#/home: 会加载 vue 子项目的 home 页面
  • /vue-history/about: 会加载 vue-history 子项目的 about 页面
  • /#/about: 会加载主项目的about页面
  1. 用 hash 来区分子项目

这样做其实不太好,有点反常规,但是也可以用:

  • /home/#/vue: 会加载 vue 子项目的 home 页面
  • /#/vue-hash/about: 会加载 vue-hash 子项目的 about 页面
  • /#/about: 会加载主项目的about页面

优点:无

缺点: 对 hash 子项目是入侵式修改,如果是全新项目,则无影响。

总结

主项目路由的 hash 与 history 模式都可以使用,各有优劣,看情况取舍。

vue 项目 hash 模式改 history:
new Router 时设置 mode 为 history

webpack 打包的配置( vue.config.js )

如果一些资源报 404,相对路径改为绝对路径

<img src="./img/logo.jpg"> 改为 <img src="/img/logo.jpg"> 即可

css 污染问题及加载 bug
1
,qiankun 只能解决子项目之间的样式相互污染,不能解决子项目的样式污染主项目的样式

主项目要想不被子项目的样式污染,子项目是 vue 技术,样式可以写 css-scoped ,如果子项目是 jQuery 技术呢?所以主项目本身的 id/class 需要特殊一点,不能太简单,被子项目匹配到。 2,从子项目页面跳转到主项目自身的页面时,主项目页面的 css 未加载的bug 临时解决办法:先复制一下 HTMLHeadElement.prototype.appendChild 和 window.addEventListener ,路由钩子函数 beforeEach 中判断一下,如果当前路由是子项目,并且去的路由是父项目的,则还原这两个对象.

const childRoute = ['/app-vue-hash','/app-vue-history'];

const isChildRoute = path => childRoute.some(item => path.startsWith(item))

const rawAppendChild = HTMLHeadElement.prototype.appendChild;

const rawAddEventListener = window.addEventListener;

router.beforeEach((to, from, next) => {

// 从子项目跳转到主项目

if(isChildRoute(from.path) && !isChildRoute(to.path)){

HTMLHeadElement.prototype.appendChild = rawAppendChild;

window.addEventListener = rawAddEventListener;

}

next();

});

主子通信
三种方式:
1,动态通信 通过rx.js

部署二级目录
必须配置 publicPath,vue-cli3 官网描述
entry 最后面加一个 /,正确是 http://location:5000/good/ 而不是 http://location:5000/good
要配置 publicPath :https://cli.vuejs.org/zh/config/#publicpath
打包之后,你的js的路径应该是/good/static/css.js,而不是/static/css.js

div[id^='_qiankun_microapp_wrapper'] {

height: 100%;

}

主子应用css隔离方案

子应用:

配置:
// postcss.config.js

module.exports = {

plugins: {

autoprefixer: {},

'postcss-selector-namespace': {

namespace: function(css) {

// element-ui的样式不需要添加命名空间

if (css.includes('element-variables.scss')) return '';

return ‘#micro-view’;

},

},

},

};

micro-view #app[data-v-7ba5bd90] {}

主应用:

业务线使用element-ui
dialog,drawer 的v-modal 层级问题:设置属性append-to-body=true

静态资源 404(字体,img等)
webpack打包注入完整路径配置

file-loader|url-loader 增加publicPath

const publicPath = http://localhost:${port}
{

test: /.(woff2?|eot|ttf|otf)$/i,

use: [

{

loader: 'file-loader',

options: {

name: 'fonts/[name].[hash:8].[ext]',

publicPath

},

},

],

},

Uncaught Error: application 'cmsClient' died in status LOADING_SOURCE_CODE: only one instance of babel-polyfill is allowed

子应用通过<script src=“babel-polyfill” ignore /> 引入, script 标签加上 ignore 属性

Throw Error "NavigationDuplicated"

使用 $router.push 更改“ page”。如果您使用的是同一个页面,vue-router 抛出一个 Error。

这个是 vue-router 的报错,你应该是 push 相同路由了

// 避免跳转相同路由报错

const originalPush = VueRouter.prototype.push

VueRouter.prototype.push = function push(location) {

return originalPush.call(this, location).catch(err => err)

}

至于 Uncaught SyntaxError: Unexpected token '<',这是 vue-cli-service 的问题

默认情况下,它打包出来的 js 资源地址是相对路径,比如 <script type="text/javascript" src="js/app.js"></script>

当你在子应用路径下的时候(比如 http://localhost:7770/basic/about) ,上面的 js 资源就指向了 /basic/js/app.js,

1. publicPath
部署到二级目录也是要配置 publicPath 的:https://cli.vuejs.org/zh/config/#publicpath

  1. webpack 的 webpack_public_path 配置是用来给动态加载的资源做路径补全的,不适用于那些直接在 html 中静态引入的 js/css/img 等,这些资源要想变更路径前缀需要配置 webpack publicPath,这个可以翻下 webpack 文档或者自己做下测试验证
  2. 你的 html 里静态引入的 /static/css.js 是绝对路径的资源,这种写法无论是 http://localhost:5000 还是 http://localhost:5000/good/ 上下文中访问,计算出来的地址都会是 http://localhost:5000/static/css.js,原因上面 @gongshun 的截图里说明了,你可以自己随便搞个 html 测试一下
  3. assetPublicPath 或许应该叫 assetContext 或者 assetRuntimePublicPath,现在这个命名确实容易让人误解其等价于 webpack 的 publicPath 配置

webpack配置的publicpath设置成当前运行时的路径

父应用使用了babel-pollfill,子应用不要在在bable-pollfill

子应用的代理将失效,代理需要配置在父应用中

项目中使用了百度地图等组件,会出现在子应用中无法使用的情况 ,在看源码时发现子应用的document.body中添加script标签失败没报错,但无法正确添加到body中,类似性质的问题还有美洽客服的引用,pdfjs的引用两种解决方法:

更新qiankun版本至2.0.17,在start中添加excludeAssetFilter,在主应用中引入script标签

  • 使用iframe单独调用百度地图的页面(没有太多页面交互推荐用这种)

<div id="output"></div>

<script type="text/javascript">

//document.write('<' + 'script src="' + src + '"' +' type="text/javascript"><' + '/script>')

document.querySelector("#output").innerHTML = '<' + 'script src="' + src + '"' + ' type="text/javascript"><' + '/script>'

</script>

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容