微前端之qiankun的使用

微前端之qiankun的使用

在之前的文章中谈到了single-spa的使用,为了多了解微前端的各类架构,在新的项目中使用了qiankun,接下来就说一说qiankun的使用

安装

1、使用vue cli 快速创建基座(vue-portal)和子应用1(vue-supervise)、子应用2(vue-threen)
2、基座应用 npm install qiankun --save

准备工作做好后,我们开始分别对基座和子项目进行配置

基座应用(vue-portal)的配置

1、main.js 同级创建micro-app.js

import store from './store'
import store1 from './vuex'
const microApps = [
  {
    
    
    name: 'vthreen',
    // entry: `http:XXXXXXX/vthreen/`, //测试环境地址
    entry: '//localhost:8500/vthreen/', //本地运行地址
    activeRule: '/vthreen',
    container: '#yourContainer',
  },
  {
    
    
    name: 'vsupervise',
    // entry: `http:XXXXXXX/vsupervise/`, //测试环境地址
    entry: 'http://localhost:8999/vsupervise/', //本地运行地址
    activeRule: '/vsupervise',
    container: '#yourContainer',
  },
]

const apps = microApps.map(item => {
    
    
  return {
    
    
    ...item,
    container: '#yourContainer', // 子应用挂载的div
    props: {
    
    
      routerBase: item.activeRule, // 下发基础路由
      getGlobalState: store.getGlobalState, // 下发getGlobalState方法(主应用的一些信息)
      store1: store1.router || []
    }
  }
})
export default apps

2、main.js中使用

import Vue from 'vue'
import App from './App.vue'
import './assets/css/main.scss'
import {
    
      registerMicroApps, start } from 'qiankun'
import ElementUI from 'element-ui'
import './router/engine'
import './utils/directive.js'
import * as components from './components'
import '@/assets/iconfont/iconfont.css' // fontclass使用
import '@/assets/iconfont/iconfont.js' //svg方式使用
import {
    
      encrypt, decrypt } from '@/utils/encryp.js';

import store from './vuex'
import Vuex from 'vuex'
import router from './router'
Vue.config.productionTip = false
Vue.prototype.encrypt = encrypt
Vue.prototype.decrypt = decrypt

Vue.use(ElementUI)

Vue.use(Vuex)
import api from './api'
Vue.prototype.$api = api

Object.keys(components).forEach(key => {
    
    
  const component = components[key]

  if (['Col', 'Form', 'Input'].includes(key)) {
    
    
    Vue.component(`I${
      
      key}`, component)
  } else {
    
    
    Vue.component(key, component)
  }

  if (['Modal', 'Message', 'Notice'].includes(key)) {
    
    
    Vue.prototype[`$${
      
      key}`] = component
  }
})

const instance = new Vue({
    
    
  router,
  store,
  render: h => h(App)
}).$mount('#app')

import microApps from './micro-app'
// 定义loader方法,loading改变时,将变量赋值给App.vue的data中的isLoading
function loader(loading) {
    
    
  if (instance && instance.$children) {
    
    
    // instance.$children[0] 是App.vue,此时直接改动App.vue的isLoading
    instance.$children[0].isLoading = loading
  }
}

// 给子应用配置加上loader方法
const apps = microApps.map(item => {
    
    
  return {
    
    
    ...item,
    loader
  }
})

registerMicroApps(apps, {
    
    
  beforeLoad: app => {
    
    },
  beforeMount: [
    app => {
    
    }
  ],
  afterMount: [
    app => {
    
    }
  ],
  afterUnmount: [
    app => {
    
    }
  ]
})
start()

3、 页面挂载设置

在那个页面挂载就在该页面的mounted

<template>
  <div id="app">
    <el-container class="layout" v-if="!$route.meta.showAll">
      <el-header>
        <common-header :config="config"></common-header>
        <ul class="header-nav">
          <li class="menu-btn pointer" @click="goPortal">
            <i class="el-icon-arrow-left"></i>返回首页
          </li>
          <li v-for="(sys, key) in sysList" class="pointer" :class="{'active': activeId == sys.appId}"
            :key="key" @click="toggleSys(sys)">{
    
    {
    
    sys.appName}}</li>
        </ul>
      </el-header>
      <router-view v-if="$route.meta.isSingle"></router-view>
       <!-- yourContainer 挂载容器 -->
      <div id="yourContainer" v-else></div> 
    </el-container>
    <router-view v-else></router-view>
  </div>
</template>

<script>
import {
    
     mapGetters } from "vuex";
import {
    
     CommonHeader } from "vue-common";
import {
    
     start } from "qiankun";
import parser from 'vue-common/dist/router/parser'
import asyncRouter from '@/router/map/async.js'
export default {
    
    
  name: 'App',
  components: {
    
    
    CommonHeader,
  },
  data() {
    
    
    return {
    
    
      modulesMenu: [],
      sysList: [],
      config: this.$config,
      breadcrumbShow: false,
      activeId: ""
    };
  },
  computed: {
    
    
    ...mapGetters({
    
    
      getSysList: 'getSysList',
    }),
  },
  watch: {
    
    
    getSysList(val) {
    
    
      this.sysList = val
    },
    // 切换路由时监听导航渲染
    '$route.path'(val) {
    
    
      this.updateActiveRouter()
    }
  },
  // 页面刷新时如果不是首页要加载侧边栏
  mounted() {
    
    
    if (!window.qiankunStarted) {
    
    
      window.qiankunStarted = true;
      start(); // 启动
    }

  },
  methods: {
    
    
    goPortal() {
    
    
      this.$router.push({
    
     name: 'home' })
    }
  },
  created() {
    
    

    // this.getConfigJson()
  }
};
</script>


4、vue.config.js设置

module.exports = {
    
    
  transpileDependencies: ['common'],
  assetsDir: 'static',
  lintOnSave: false, // 是否使用eslint
  productionSourceMap: false, // 关闭生产源映射加速生产构建
  devServer: {
    
    
    port: 9999, // 在.env中VUE_APP_PORT=7788,与父应用的配置一致
    headers: {
    
    
      'Access-Control-Allow-Origin': '*' // 主应用获取子应用时跨域响应头
    },
    proxy: {
    
    
      '/api': {
    
    
        target: 'http://192.162.130.141:9001',
        // target: 'http://172.160.1.4:9001',
        changeOrigin: true, //开启代理
        pathRewrite: {
    
    
          '^/api': ''
        }
      },
    },
    https: false, // 不支持https协议
    open: true, // 配置自动启动浏览器

  },
  chainWebpack: config => {
    
    
    const oneOfsMap = config.module.rule('scss').oneOfs.store
    oneOfsMap.forEach(item => {
    
    
      item
        .use('sass-resources-loader')
        .loader('sass-resources-loader')
        .options({
    
    
          // 全局变量文件路径,只有一个时可将数组省去
          resources: ['./src/assets/css/main.scss']
        })
        .end()
    })
    config.plugin('html')
      .tap((args) => {
    
    
        args[0].title = 'qiankun-example'
        return args
      })
  }
}

5、路由设置

基座应用也是history模式,但是base不需要设置
在这里插入图片描述

子应用的配置

1、vue.config.js同级创建一个.env文件,端口号要与基座micro-app.js中的配置一致

在这里插入图片描述

2、main.js同级创建public-path.js

(function () {
    
    
  if (window.__POWERED_BY_QIANKUN__) {
    
    
    if (process.env.NODE_ENV === 'development') {
    
    
      // eslint-disable-next-line
      __webpack_public_path__ = `//localhost:${
      
      process.env.VUE_APP_PORT}${
      
      process.env.BASE_URL}`
      return
    }
    // eslint-disable-next-line
    __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
    // __webpack_public_path__ = `${process.env.BASE_URL}/`
  }
})()

3、main.js配置

import './public-path'
let instance = null

function render(props = {
    
    }) {
    
    
  const {
    
    
    container,
    routerBase
  } = props
  // const router = new VueRouter({
    
    
  //   base: window.__POWERED_BY_QIANKUN__ ? routerBase : process.env.BASE_URL,
  //   mode: 'history',
  //   routes
  // })
  //路由配置

  instance = new Vue({
    
    
    router,
    store,
    render: (h) => h(App)
  }).$mount(container ? container.querySelector('#vcatalog') : '#vcatalog')
}
if (!window.__POWERED_BY_QIANKUN__) {
    
    
  // 这里是子应用独立运行的环境,实现子应用的登录逻辑

  // 独立运行时,也注册一个名为global的store module
  // commonStore.globalRegister(store)
  // 模拟登录后,存储用户信息到global module

  render()
}

export async function bootstrap() {
    
    }

export async function mount(props) {
    
    
  render(props)
}

export async function unmount() {
    
    
  instance.$destroy()
  instance.$el.innerHTML = ''
  instance = null
}

4、路由配置放在router index.js中了

所有子项目必须是history模式,base设置与基座micro-app.js中的配置一致
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_32881447/article/details/121113651