Skip to content

qiankun接入遇到的问题

挂载节点

父应用(vue)的挂载节点(public/index.html)的id 不能与 子应用的挂载容器id相同,否则会抛错. [qiankun] Target container with #vue-project1 not existed after vue-project1 mounted!

应修改:public/index.html中div的id,对应main.js的$mount的id也需要修改;App.vue的根元素id值(任意)

TIP

知识

  1. vue-cli生成的项目,public/index.html中app容器<div id="app"></div>会被vue-template生成的模板整个替换掉,此时根元素是Vue虚拟DOM中的根元素

  2. 如果是手动script:src加载的new Vue({...}), 不会替换掉html中app容器

重复注册vue-router钩子

如果vue-router的钩子(例如:beforeEach)在new Vue的生命周期(例如: create)中执行,根据子应用生命周期的执行逻辑,mount有可能会执行多次。所以也会导致vue-router的钩子注册多次,可能造成内存泄漏

js
// ...
let instance = null
const render = () => {
  instance = new Vue({
    router,
    mounted() {
      // 注册的路由钩子
      router.beforeEach(() => {}) // ...
    },
    render: (h) => h(App)
  }).$mount('#app')
}
// ...
// mount
export async function mount(props) {
  render(props)
}
// unmount
export async function unmount() {
  router.beforeHooks = [] // 清空vue-router钩子【看源码】
  instance.$destroy()
}

解决:跳转业务逻辑或在子应用unmount时手动清空vue-router的前置钩子回调

js
router.beforeHooks = []

vue-router的base

配置后vue-router解析base后的path,来匹配组件

js
new VueRouter({
  mode: 'history',
  base: window['__POWERED_BY_QIANKUN__'] ? '/app1/' : '/',
  routes
})

vue组件的script块打印window是原生的window?【待解决】

在主应用中加载的子应用,其js执行时,理论上讲打印的应该是Proxy

  • 得打包看看,这里的逻辑了
js
// console.log(window)
// console.log(setInterval)
// console.log(window.setInterval === setInterval)
setInterval(() => {
  console.log(1)
}, 2000)
export default {
  created() {
    setInterval(() => {
      console.log(2)
    }, 2000)
    // console.log((0, eval)('window'))
    console.log(window) // proxy 沙箱上下文
    console.log(window.setInterval)
    console.log(setInterval)
    console.log(`environment: ${ process.env.VUE_APP_CUR_ENV }`)
  }
}

location问题【待】

可能会破坏路由

  • 解决方案?考虑劫持?

css等资源加载

css直接打包进js,即css in js

js
// vue.config.js
css: {
  extract: false
},

axios的baseUrl

单独部署保持默认/,请求后端服务;

在主应用请求时需在主应用的nginx配置转发(建议),或允许所有请求跨域(不建议)

js
// /child-app1转发至app1的后端服务
import axios from 'axios'
if (window['__POWERED_BY_QIANKUN__']) axios.defaults.baseURL = '/child-app1'

WARNING

这块对子应用的侵入性较大。nginx转发时需要对子应用做蛮大的改造。跨域解决时需要在后端配置跨域。

本地开发:主应用中加载子应用

主应用配置

js
// ...
  devServer: {
    // ...
    proxy: {
      // ...
      // 转发子应用服务【参考上面的axios配置】
      '/child-app1': {
        target: 'http://localhost:9001/', // 转发到子应用,再由子应用自个转发...(好麻烦)
        pathRewrite(api) {
          return api.replace(/\/child-app1/, '')
        }
      }
    }
  },

子应用配置

js
// ...
  publicPath: isProd ? '/child-admin/' : `//${host}:${port}`, // 本地采用跨域模式,线上采用nginx转发
  devServer: {
    headers: {
      'Access-Control-Allow-Origin': '*' // 允许跨域
    },
    proxy: {
      // ...
      '/': {
        target: `http://${host}:${port}`,
        bypass(req) {
          if (req.headers.accept.includes('html')) return '/index.html' // 解决本地配置publicPath 404问题
        }
      }
    }
  }
// ...

子应用使用web-worker【待实验】

子应用使用web-worker,主应用无法跨域加载并执行(暂未找到解决方案)

  • 解决:nginx路由转发后,子应用相对地址引用的web worker应该是可用的