微前端(qiankun)接入项目、主应用与子应用之间通信

前言

随着公司项目建设,各系统出现了用户、角色、组织、权限等系统基础功能重复开发的问题,为了不重复开发、减少成本,也为了整合项目,提升用户体验,也为了之前单独开发的系统能够统一访问。所以我们使用“微前端”。

什么是微前端?

微前端是一种多个团队通过独立发布功能的方式来共同构建现代化web应用的技术手段及方法策略。
微前端借鉴了微服务的架构理念,将一个庞大的前端应用拆分为多个独立灵活的小应用,每个应用都可以独立开发、独立运行、独立部署,再将这些小型应用联合微一个完整的应用。微前端既可以将项目融合为一,又可以减少项目之间的耦合,提升项目扩展性,相比一整块的前端仓库,微前端架构下的前端仓库倾向于更小更灵活。

微前端优势(链接: qiankun)

  1. 技术无关
    主应用不限制接入应用的技术栈,子应用具备完全的自主权(子应用所用的技术不限制)

  2. 独立开发、独立部署
    微应用仓库独立升级,前后端可独立开发,部署完成后主框架自动完成同步更新

  3. 增量升级
    在面对个证负责场景时,我们通常很难对一个已经存在的系统做出全量的技术栈升级或重构,而微前端时一种非常好的实施渐进式重构的手段和策略

  4. 独立运行时
    每个子应用之间状态隔离,运行时状态不共享

主应用
  1. 安装qiankun
    $ yarn add qiankun # 或者 npm i qiankun -S

  2. 在主应用main.js中注册微应用

import { registerMicroApps, start } from 'qiankun'
const apps = [
    {
        name: 'jdptApp',
        entry: '//localhost:30000', // 默认加载这个HTML解析里面的js动态的执行(子应用必须支持跨域)
        // fetch //子应用是否允许跨域
        container: '#vueJdpt', // 挂载的dom区域
        activeRule: '/jdpt' // 激活的路径
    }
]
registerMicroApps(apps) 注册微应用
start({
    prefetch: false, // 取消预加载
    sandbox: true //隔离样式sandbox : { experimentalStyleIsolation: true } 
}) // 开启微应用

  1. 主应用中改造App.vue文件
<template>
  <div>
    <el-menu router
             mode="horizontal">
      <!-- 基座中可以放自己的路由 -->
      <el-menu-item index="/">Home</el-menu-item>
      <!-- 引用其他子应用 -->
      <el-menu-item index="/jdpt">机电平台</el-menu-item>
    </el-menu>
    <!-- // 显示自己的路由 -->
    <router-view></router-view>
    <!-- 显示子应用路由 -->
    <div id="vueJdpt"></div>
  </div>
</template>
  1. 改造子应用
    main.js中
let instance = null
function render() {
  instance = new Vue({
    router,
    store,
    render: h => h(App)
  }).$mount("#app") // 挂载到自己的html中,基座会拿到这个挂载后的html 将其插入进去
}
// 如果在乾坤下运行,
if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
// 如果独立运行,则直接渲染
if (!window.__POWERED_BY_QIANKUN__) {
  render()
}
// 地下三个方法为自组建的协议 内部会校验此方法 必须传,单里面可以不写内容
**
 1. bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
 2. 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
 */
export async function bootstrap(props) { }
/**
 3. 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
 */
export async function mount(props) {
  render(props)
}
/**
 4. 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
 */
export async function unmount(props) {
  instance.$destroy()
  instance = null
}
  1. 改造子应用vue.config.js文件
module.exports = {
    devServer: {
        port: 30000,
        headers: {
            'Access-Control-Allow-Origin': '*' // 允许所有跨域
        }
    },
    configureWebpack: {
        output: {
            library: 'jdptApp',
            libraryTarget: 'umd', 打包成 umd 库格式
        }
    }
}
  1. 改造子应用router.js文件
const routes = [{
        path: '/',
        name: 'Home',
        component: Home
    },
    {
        path: '/about',
        name: 'About',
        // route level code-splitting
        // this generates a separate chunk (about.[hash].js) for this route
        // which is lazy-loaded when the route is visited.
        component: () =>
            import ( /* webpackChunkName: "about" */ '../views/About.vue')
    },
    {
        path: '/demo',
        name: 'Demo',
        component: () =>
            import ( /* webpackChunkName: "about" */ '../views/Demo.vue')
    }
]
console.log(routes, 'routes');
const router = new VueRouter({
    mode: 'history',
    base: window.__POWERED_BY_QIANKUN__ ? '/jdpt' : process.env.BASE_URL, // 如果在乾坤环境下运行则添加'/jdpt'前缀
    routes
})

export default router

主应用与父应用之间通信

主应用

  1. 在src下新建action.js
// 乾坤全局通信
import { initGlobalState } from 'qiankun'
const initState = {
    // 这里写初始化数据

}
const actions = initGlobalState(initState)
actions.onGlobalStateChange((state, preState) => {
    console.log(preState, '主应用变更前');
    console.log(state, '主应用变更后');

})
export default actions
  1. 在main.js中将其引入
 import actions from '@/action'
 const apps = [
    {
        name: 'jdptApp',
        entry: '//localhost:30000', // 默认加载这个HTML解析里面的js动态的执行(子应用必须支持跨域)
        // fetch //子应用是否允许跨域
        container: '#vueJdpt', // 挂载的dom区域
        activeRule: '/jdpt', // 激活的路径
        props: {
            actions,
            msg: '消息'
        } // 向子应用传递创建的全局状态(*注意*)
    }
]
  1. 在Home.vue
<template>
  <div class="home">基座自己的路由页面
    <el-button @click="handle1">点击向子应用发送消息</el-button>
    <p>当前显示的项目</p>
  </div>
</template>

<script>
// @ is an alias to /src
import actions from '../action'
export default {
  name: 'Home',
  data () {
    return {
      mes1: {
        project_id: '机电平台'
      }
    }
  },
  mounted () {
    // 注册一个观察者函数
    actions.onGlobalStateChange((state, preState) => {
      console.log(preState, '主应用观察者变更前');
      console.log(state, '主应用观察者变更后');
    })
  },
  methods: {
    handle1 () {
      actions.setGlobalState(this.mes1)
      this.$router.push('/jdpt')
    },
  }
}
</script>

子应用

  1. 在src下新建action.js
function emptyAction() {
  // 警告提示,当前使用的是空action
  console.warn('current execute action is empty');
}
// 设置一个用于通信的 action类

class Action {
  actions={
    onGlobalStateChange:emptyAction,
    setGlobalState:emptyAction
  }
  constructor(){

  }
  // 默认为空Action

  // 设置actions
  setActions(actions){
    this.actions = actions
  }
  // 映射
  onGlobalStateChange(...args:[]){
    return this.actions.onGlobalStateChange(...args)
  }
  // 映射
  setGlobalState(...args:[]){
    console.log(args,'args');
    
    return this.actions.setGlobalState(...args)
  }
}
const actions = new Action()
export default actions
  1. 在main.js中
import actions from './action'
function render(props) {
  console.log(props,'props');
  
  if (props) {
    actions.setActions(props)
  }
  instance = new Vue({
    router,
    store,
    render: h => h(App)
  }).$mount("#app") // 挂载到自己的html中,基座会拿到这个挂载后的html 将其插入进去
}
export async function mount(props) {
// 子应用进入时将数据进行传递
  render(props)
}
  1. 在App.vue中
<template>
 <el-button @click="handle1">点我向父应用发送数据</el-button>
 </template>
<script>
 import actions from "@/action"
 // 在mounted中
   mounted () {
    actions.onGlobalStateChange(state=>{
      console.log(state,'子应用检测数据');
    },true) // onGlobalStateChange 第二个参数设置为true,会立即触发一次观察者函数
  },
  methods: {
    handle1(){
      actions.setGlobalState({project_id:'机电子应用'})
    }
   }
</script>


后续会更新项目中遇到的问题。。。

你可能感兴趣的:(前端,javascript,vue.js,前端框架)