Copyright notice: This article is originally published BY the blogger. It follows the COPYRIGHT agreement CC 4.0 BY-SA. Please attach the link of the original source and this statement. This paper links: gudepeng. Making. IO/note / 2020/0…

1. Previous information

The last post has talked about what is micro front-end, students who are not familiar with the micro front-end architecture design and practice: Origin of this case will continue to update into an open source management project, welcome star, if you need support and need new features can contact me at the project address: github.com/gudepeng/vu… Official link: github.com/umijs/qiank… Versions and methods after qiankun2.0 are used in this paper

2. The actual combat

1. Create projects

Since both the main project and the sub-project are developed using VUE, create the project using VUE-cli4. Create 2 projects, one main project and one sub-project.

npm install -g @vue/cli @vue/cli-service-global
vue create vue-giant-master
vue create vue-giant-module
Copy the code

2. Main project preparation

Now I will start to write the project normally. I have seen many demo and actual projects on the Internet before, and everyone’s main project is directly put the universe code in the main. Then use V-if in app.vue to control whether the route is displayed or the loaded subproject is displayed. Because it is a background management project, in my actual project development, there are many routing pages of the main project, so I feel that this way is not very convenient. So I used the normal VUE project to start and then loaded the code of Qiankun on the home page after login. Here is the implementation.

<template>
  <div class="panel" @click="doOnWeb" @keydown="doOnWeb">
    <top-header class="panel-heder"></top-header>
    <div class="panel-main"> <! --> <router-view v-if="showView" />
      <div v-else id="root-view"></div>
    </div>
    <main-menu ref="mainMenu" class="main-menu" v-show="showMenu"></main-menu>
    <main-login ref="mainLogin"></main-login>
  </div>
</template>

<script>
import TopHeader from '@/layout/components/Header'
import MainMenu from '@/layout/components/Menu'
import MainLogin from '@/layout/components/MainLogin'Import {registerMicroApps, start} from'qiankun'
import axios from '@/utils/request'

export default {
  name: 'Layout',
  components: {
    TopHeader,
    MainMenu,
    MainLogin
  },
  data() {
    return {
      showMenu: false
    }
  },
  computed: {
    showView: function() {
      return this.$route.path === '/home'}},mounted() {// Define the data, methods, and components passed into the child application const MSG = {data: this.$store.getters,
      fns: [],
      prototype: [{ name: '$axios', value: axios}]} registerMicroApps([{name: axios}]})'module-app1',
          entry: '//localhost:8081',
          container: '#root-view',
          activeRule: '/app1',
          props: msg
        },
        {
          name: 'module-app2',
          entry: '//localhost:8082',
          container: '#root-view',
          activeRule: '/app2',
          props: msg
        }
      ],
      {
        beforeLoad: [
          app => {
            console.log('before load', app)
          }
        ],
        beforeMount: [
          app => {
            console.log('before mount', app)
          }
        ],
        afterUnmount: [
          app => {
            console.log('after unload', app)}]}) // start({prefetch:true })
  },
  methods: {
    toggleMenu() { this.showMenu = ! this.showMenu this.$refs['mainMenu'].showTwoMenu = false
    },
    doOnWeb() {
      this.$refs.mainLogin.doOnWeb()
    }
  }
}
</script>
Copy the code

By doing so, you can make a good distinction between subprojects and main project loads. There can also be common deployment displays (such as Menu and title)

Pay attention to the implementation

PushState () {if (# rootview) {if (# rootview) {if (# rootview) {if (# rootview) {if (# rootview) {if (# rootview) {if (# rootview) {if (# rootview) { The Qiankun will report an error and will not load when jumping to the sub-project page.

2. At the time of transmission method to the component or components, do not use/function array directly within the method of direct way, because in the actual deployment package when the method name can be webpack packing compression, receive the name of the method in the component will be wrong, component to invoke additional qualifications purpose method would find this method.

3. When passing data, this article uses the getter for passing store, and then the subproject receives the initial store of the subproject at the appropriate time. When the value of the main item changed, the initGlobalState provided by Qiankun was used to transfer the value (in the version of Qiankun 1.x, the communication after loading the sub-item was not supported. I used the window event to transfer the value). Update to store in subproject.

I have also tried to pass store directly to the subproject, and it will, so that the main project and the subproject act as public stores, but the pages in the subproject don’t listen for store changes. You need to manually use $forceUpdate(). I have taken a look at vuex’s source code. It seems that it is because he only created a monitoring of DOM changes. I haven’t looked into the details yet, and I will make a detailed investigation later.

The design of the router

import router from '.. /router'
import store from '.. /store'
import { Message } from 'element-ui'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { getUserToken } from '@/utils/user'
import Layout from '@/layout/index.vue'

NProgress.configure({
  showSpinner: false
})

router.beforeEach(async (to, from, next) => {
  NProgress.start()
  const hasToken = getUserToken()
  if (hasToken) {
    if (to.path === '/login') {
      next({
        path: '/home'
      })
      NProgress.done()
    } else {
      const isLogin = store.getters.userInfo
      if (isLogin) {
        next()
      } else{try {// Add to router permission await store.dispatch('user/getUserInfo')
          router.addRoutes([
            {
              path: '/',
              name: 'Layout',
              component: Layout,
              children: [
                {
                  path: 'app1*'} ] } ]) next({ ... to, replace:true
          })
        } catch (error) {
          Message.error(error || 'Has Error') next(`/login? redirect=${to.path}`)
          NProgress.done()
        }
      }
    }
  } else {
    if (to.path === '/login') {
      next()
      NProgress.done()
    } else {
      next({
        path: '/login'
      })
    }
  }
})

router.afterEach(() => {
  NProgress.done()
})

Copy the code

If the user does not log in, the user will be intercepted to the login page. If the user has logged in, it will determine whether the user has obtained the permission. If the user has not obtained the permission, it will obtain the permission, and then add the route of the corresponding subproject to the router. Since my subproject page is under the layout page, I need to add the corresponding permissions for any subproject that I don’t have

router.addRoutes([
    {
        path: '/',
        name: 'Layout',
        component: Layout,
        children: [
        {
            path: 'app1*'}}]])Copy the code

Subproject writing

The subproject is just a normal Vue project, with methods and components received in main.js mounted to vue.prototype and data received initialized to store.

import './public-path'
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import './plugins/element.js'
import '@/utils/permission.js'

Vue.config.productionTip = false

let instance = null

export async function bootstrap({ prototype }) {
  prototype.map(p => {
    Vue.prototype[p.name] = p.value
  })
}

function initStore(props) {
  props.onGlobalStateChange && props.onGlobalStateChange((value, prev) => {})
  props.setGlobalState &&
    props.setGlobalState({
      ignore: props.name,
      user: {
        name: props.name
      }
    })
}

function render(props = {}) {
  instance = new Vue({
    router,
    store,
    render: h => h(App)
  }).$mount('#app')}export async function mount(props) {
  initStore(props)
  if (props.data.userInfo.roles) {
    store.commit('permission/SET_ROLES', props.data.userInfo.roles)
  }
  render()
}

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

if(! window.__POWERED_BY_QIANKUN__) { render() }Copy the code

You need to create public-path.js in the parent directory of main.js

if (window.__POWERED_BY_QIANKUN__) {
  // eslint-disable-next-line no-undef
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
Copy the code
The design of the router

The master project has passed permissions to its subprojects. When initializing the route, you only need permission to load the route of the object

import router from '@/router'
import store from '@/store'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'

NProgress.configure({ showSpinner: false })

router.beforeEach(async (to, from, next) => {
  NProgress.start()

  const hasRoles = store.getters.routes && store.getters.routes.length > 0

  if (hasRoles) {
    next()
  } else {
    // get user info
    const roles = await store.state.permission.roles
    const accessRoutes = await store.dispatch(
      'permission/generateRoutes', roles ) router.addRoutes(accessRoutes) next({ ... to, replace:true })
  }
})

router.afterEach(() => {
  NProgress.done()
})

Copy the code

Vue. Config. Js

You need to add the output of the file to it

output: {
    library: `${name}-[name]`,
    libraryTarget: 'umd',
    jsonpFunction: `webpackJsonp_${name}`}Copy the code

Subprojects need to support cross-domain, so they need to be added

headers: {
      'Access-Control-Allow-Origin': The '*'
    }
Copy the code

other

The above is the core code of the whole project, the other part is the development method of the specific VUE project, this article will not elaborate. For example, if the user does not operate for a period of time, the dialog and menu of login will pop up