Vue 2.0 + 乾坤(qiankun)+ Module Federation 踩坑记录(一)

1,137 阅读3分钟

乾坤 vs 无界

背景:

  1. 需要拆解巨石项目
  2. 新模块需要独立部署、运行
  3. 新模块需要在巨石项目中集成
  4. 许多业务功能重复,需要提纯、共享
  5. 兼容IE11

乾坤【✅】 无界【❌】

不是无界不好,是我处理不了IE中iframe弹窗的问题,我反而最喜欢的是无界的方案,接入成本真的太低了,用起来很爽,如果不用考虑IE,我觉得无界是一个非常不错的选择。

接入乾坤

子应用

// public-path.js

if (window.__POWERED_BY_QIANKUN__) {
  // eslint-disable-next-line no-undef
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}

// main.js
import './public-path'
...

let instance = null
const render = (props = {}) => {
// mf的代码先别管
  const { container, moduleFederations } = props
  setModuleFederationList({ mfs: moduleFederations })
  instance = new Vue({
    render: hx => hx(App),
    router,
  }).$mount(container ? container.querySelector('#children-01') : '#children-01')
}

// 独立运行
if (!window.__POWERED_BY_QIANKUN__) {
  render()
}

// 生命周期
export async function bootstrap() {
  console.log('[vue] vue app bootstraped')
}
export async function mount(props) {
  console.log('[vue] props from main framework', props)
  render(props)
}
export async function unmount() {
  console.log('[vue] vue app unmount')
  instance.$destroy()
  instance.$el.innerHTML = ''
  instance = null
}
// webpack.config.js (webpack 5)
...
  output: {
    // 出口文件
    path: process.cwd() + '/dist',
    publicPath: isDev ? '/' : '/v/xxx/',
    filename: 'js/[name].[hash:8].js',
    clean: true,
    library: `micro-xxx`,
    libraryTarget: 'umd',
    chunkLoadingGlobal: `webpackJsonp_${name}`,//这个是webpack5 更新的属性 之前是jsonpFunction
  },
 ...

基座应用

我单独封装了一层,可以按照文档直接用

npm install qiankun -D
// register-micro-app.js

import { loadMicroApp } from 'qiankun'
const isDev = process.env.NODE_ENV === 'development'

const microApps = {
  'micro-app-xxx': {
    name: 'micro-xxx',
    entry: isDev
      ? '//localhost:8889'
      : `${window.location.protocol}//${window.location.host}/v/xxx/`,
    container: '#xxx',
    activeRule: '/v/micro/xxx',
    configuration: {
      // sandbox: { strictStyleIsolation: true },
      sandbox: false,
    },
  },
}

/**
 * @returns microApp:Object
 * mount(): Promise<null>;
 * unmount(): Promise<null>;
 * update(customProps: object): Promise<any>;
 * getStatus(): | "NOT_LOADED" | "LOADING_SOURCE_CODE" | "NOT_BOOTSTRAPPED" | "BOOTSTRAPPING" | "NOT_MOUNTED" | "MOUNTING" | "MOUNTED" | "UPDATING" | "UNMOUNTING" | "UNLOADING" | "SKIP_BECAUSE_BROKEN" | "LOAD_ERROR";
 * loadPromise: Promise<null>;
 * bootstrapPromise: Promise<null>;
 * mountPromise: Promise<null>;
 * unmountPromise: Promise<null>;
 */

const registerMicroApp = ({ name, moduleFederations }) => {
  return loadMicroApp({ ...microApps[name], props: { moduleFederations } })
}

export default registerMicroApp


// demo.vue
...
    <div class="micro-content" id="cooperation" />
...

import registerMicroApp from '@/register-micro-app'
...
// 在合适的位置上使用
this.microApp = registerMicroApp({
  name: 'micro-app-cooperation-space',
  // 这里暂时不用管
  moduleFederations: {
    'koala/demo2': showDemoModal,
  },
})

以上,基本上可以看到子应用在基座应用中显示了

官方文档给出的

地址

先看看,能解决一大部分问题

element-ui 与 ant-design-vue 样式冲突

需要注意ant-design-vue的版本不同,dist/antd.less可能不一样

// design 与 view等老项目样式冲突,这里将原要引入的ant-design-vue/dist/antd.less借助reset进行一个套壳,限定范围
*[class*='ant-'] {
  @import 'ant-design-vue/lib/style/index.less';
  @import 'ant-design-vue/lib/style/components.less';
  html,
  body {
    padding: 0;
    margin: 0;
    font-family: Arial, 'PingFangSC-Regular', 'microsoft yahei', '微软雅黑', 'Hiragino Sans GB',
      sans-serif;
    -moz-osx-font-smoothing: '-moz-osx-font-smoothing';
    -webkit-font-smoothing: antialiased;
    background-color: #f6f7fa;
  }
  ...
 }

**!!!以下问题,只针对我的项目,可能不具备通用性

IE兼容 es6语法兼容问题

# IE11报“SCRIPT445: 对象不支持此操作”,怎么破?

//main.js
...
import 'regenerator-runtime/runtime'
import 'js/lib/base64-polyfill'
...

ie [qiankun]: Target container with #cooperation not existed while micro-cooperation mounting!

这个问题,官方有方案,但是我按照文档没有成功,最后使用loadMicroApp替代registerMicroApps解决

Uncaught Error: only one instance of babel-polyfill is allowed

项目中引入Vue CDN,在webpack中external,子应用、基座应用都是如此,会报这个错,将子应用index.html/entry.html 中的script标签增加ignore 属性即可

报错忘记了。。。😅

github.com/umijs/qiank…

只找到这个问题链接 里面说明了问题的方案 总结: js引入顺序问题,子应用打包后,css文件在最后,js倒数第二,会有问题

新版本的 html-webpack-plugin 默认会将编译的 js 产物放到 head 标签尾部,并添加 defer 属性,这会导致 qiankun 在应用加载方面产生问题(qiankun 的生命周期 js entry 需要放在页面 js 资源的末尾,即顺序排在最后),尤其当 html 文件底部自己有额外引入 js 文件的情况,一定会出问题。

在 html-webpack-plugin 配置中添加参数 inject: 'body' 搞定。 如果你的编译产物已经是在 </body> 结束标签前,那么无需处理。

// 子应用
// webpack.config.js
plugins: [
    new HtmlWebpackPlugin({
      // 自动插入到dist目录中
      title: 'micro01',
      template: './index.html',
      inject: 'body',//这里,能够将子应用打包的js放到body中
      isDev,
    }),
    ....
 ]

额。。。先记录到这,希望能够帮到大家

其实还有很多,解决之后都忘记了,上面的也是我搜索记录查出来的,如果后面想起来,继续更新。说实话有点后悔,边改边记录就对了,主要是IE恶心,还有webpack5 我是升级上来的,也解决了不少问题。

下一篇将接入Module Federation