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 对象(不用 链接直接跳转的原因是 链接会刷新页面)。

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

不管是父项目的 router 对象,还是原生的 history 对象,跳转都是 js 的方式。这里有一个小小的用户体验问题:标签()形式的跳转是支持浏览器默认的右键菜单的,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.push()、router.repace())如果使用的是 path ,则需要修改,得加上这个前缀,如果使用的是 name跳转,则无需改动:router.push({ name: 'user'})。

优点: 所有项目之间的跳转都可以直接使用自己的 router 对象或者 ,不需要借助父项目的路由对象或者原生的 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,相对路径改为绝对路径

改为 即可

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

子应用通过

当你在子应用路径下的时候(比如 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单独调用百度地图的页面(没有太多页面交互推荐用这种)

你可能感兴趣的:(qiankun 踩坑指南)