qiankun框架使用实践总结

1,337 阅读2分钟

为了项目不同模块的耦合度降低,方便开发人员后续的开发,降低代码维护的成本,方便项目管理。因此,调研了微前端框架 qiankun。 本demo主应用使用了taro框架的vue2.x版本,微应用使用了vue2.x脚手架快速搭建。

一、改造主应用

1、qiankun框架的引入:

yarn add qiankun # 或者 npm i qiankun -S

2、注册微应用:

//app.ts
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
  {
    name: 'vueApp',
    entry: '//localhost:8080',
    container: '#container',
    activeRule: '/#/pages/test/test',
  },
]);

3、新建页面并声明路由

//pages/test/test.vue
 <template>
  <div>
    <div id="container"></div>
  </div>
</template>
<script>
import { start } from 'qiankun';
export default {
    data(){
        return{
        }
    },
    mounted(){
        if (!window.qiankunStarted) {
          window.qiankunStarted = true;
          start({
            sandbox:true
          });
        }
        //当主服务跳转到当前页面时,重定向到微应用home页面
        Taro.navigateTo({ url:'pages/test/test/home'})
    }
}
</script>
//app.config.ts
export default {
  pages: [
       pages/test/test,
  ]
 }

二、搭建微应用

使用了vue2.x框架快速搭建一个微应用项目

1、创建vue2项目

vue init webpack demo

2、改造微应用的main.js文件,将bootstrap、mount、unmount三个钩子函数暴露给主应用使用

//main.js
import './public-path';
import Vue from 'vue';
import VueRouter from 'vue-router';
import App from './App.vue';
import routes from './router';
Vue.config.productionTip = false;
let router = null;
let instance = null;
function render(props = {}) {
  const { container } = props;
  router = new VueRouter({
    base: window.__POWERED_BY_QIANKUN__ ? '/pages/test/test/' : '/',
    mode: 'hash',
    routes,
  });
  const VueRouterPush = VueRouter.prototype.push
  VueRouter.prototype.push = function push (to) {
    return VueRouterPush.call(this, to).catch(err => err)
  }
  instance = new Vue({
    name: 'vueApp',
    router,
    render: (h) => h(App),
  }).$mount(container ? container.querySelector('#vueApp') : '#vueApp');
}

// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}
export async function bootstrap() {
  console.log('bootstrap');
}
export async function mount(props) {
  console.log('[vue] props from main framework', props);
  render(props);
}
export async function unmount() {
  console.log('unmount');
  instance.$destroy();
  instance.$el.innerHTML = '';
  instance = null;
  router = null;
}
//public-path.js
if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

3、注册firstPage和home两个微应用页面

//router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import childContainer from '@/components/childContainer.vue'
import home from '../pages/home/home.vue';
import firstPage from '../pages/firstPage/firstPage.vue';
Vue.use(Router)
const routes = [
  {
    path: '/pages/test/test',
    name: 'childContainer',
    component: childContainer,
    children:[
      {
        path: '/pages/test/test/home',
        name: 'home',
        component: home
      },
      {
        path: '/pages/test/test/firstPage',
        name: 'firstPage',
        component: firstPage
      },
    ]
  },
]
export default routes
//components/childContainer.vue;
<template>
  <div>
    <router-view></router-view>
  </div>
</template>
<script>
export default {
  data () {
    return {
    
    }
  },
  methods:{
  }
}
</script>
//pages/home/home.vue
<template>
  <div>
    <div @click="goto_pages">home页面</div>
  </div>
</template>
<script>
export default {
  data () {
    return {
      
    }
  },
  methods:{
    goto_pages(){
      this.$router.push('/pages/test/test/firstPage')
    }
  }
}
</script>
//pages/firstPage/firstPage.vue
<template>
  <div>
    <div @click="goto_pages">firstPage页面</div>
  </div>
</template>
<script>
export default {
  data () {
    return {
      
    }
  },
  methods:{
    goto_pages(){
      this.$router.push('/pages/test/test/home')
    }
  }
}
</script>

4、改造webpack配置文件

//build/webpack.base.conf.js
const { name } = require('../package');
//替换output
output: {
    path: config.build.assetsRoot,
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath,
      library: `${name}-[name]`,
      libraryTarget: 'umd', // 把微应用打包成 umd 库格式
      jsonpFunction: `webpackJsonp_${name}`,
  },
//webpack.dev.conf.js
devServer:{
    //添加下面代码
    headers: {
      'Access-Control-Allow-Origin': '*',
    },

}

qiankun 框架实践问题总结

  1. 微应用打包后,当微应用项目中的图片资源过大时,主应用访问微应用时,会加载不出来(可以使用外链的方式引入图片)
  2. 部署微应用时,需要在nginx上配置跨域
location / {  
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';

    if ($request_method = 'OPTIONS') {
        return 204;
    }
}
  1. 主应用储存的localStroage,微应用可以拿到
  2. 主服务传值给微服务,可以采取qiankun框架中的通讯池模式
  3. 当使用微信打来项目时,会出现头部的导航头,用其他浏览器打开,则没有
  4. 微服务必须是在主应用的某个页面的mounted生命周期上挂载,不能挂载到app.js中
  5. 在微应用路径下刷新,404问题,让微应用的base和主应用挂在微应用的页面路由保持一致
  6. 微应用跳转到主应用其他页面可以使用window.history.pushState()方法,并刷新页面,才能正常跳转过去。
    window.history.pushState({ msg:'你好' }, '',`#/pages/ha/test02/test02`)
    location.reload();
  1. 在微应用除首页外,刷新页面,将会跳转到微应用首页
  2. 注意:如果需要引入css预编译器,请安装正确的版本,最新版本和webpack不兼容,会导致安装报错。