• 微前端——qiankun(乾坤)实例


    一、什么是微前端

    微前端就是将不同的功能按照不同的未读拆分成多个子应用,通过主应用来加载这些子应用,微前端的核心在于拆,拆完后再合

    二、为什么使用微前端

    1. 不同团队间开发一个应用技术栈不同
    2. 希望每个团队都可以独立开发,独立部署
    3. 项目中还需要老的应用代码

    我们可以将一个应用划分成若干哥子应用,将子应用打包成一个个的lib,当路径切换时加载不同的子应用,这样每个子应用都是独立的,技术栈也不用做限制了!从而解决了前端协同开发的问题

    三、qiankun框架

    文档地址:https://qiankun.umijs.org/zh

    2018 年 Single-SPA 诞生了, single-spa 是一个用于前端微服务化的 JavaScript 前端解决方案 ( 本身没有处理样式隔离, js 执行隔离 ) 实现了路由劫持和应用加载。

    2019 年 qiankun 基于 Single-SPA, 提供了更加开箱即用的 API ( single-spa + sandbox + import-html-entry ) 做到了,技术栈无关、并且接入简单(像 i frame 一样简单)。

    四、qiankun框架实例

    这里打算建立三个项目进行实操,都是用的vue技术栈(这里不限技术栈),qiankun-base充当主应用,qiankun-vue和qiankun-vue1充当子应用

    1、创建三个应用

    1. 创建基座

      1. vue create qiankun-base
    2. 创建子应用1

      1. vue create qiankun-vue
    3. 创建子应用2 

      1. vue create qiankun-vue1

        三个项目:基座:qiankun-base,子应用:qiankun-vue,qiankun-vue1

    注意:这里如果时选的vue2.x创建的项目,之后用router和store的时候要注意版本号和写法的对应

    2、项目配置(主要)

    1、基座qiankun-base配置

    项目创建好后我们首先进行主应用qiankun-base的配置,进入man.js文件进行配置,在main.js中加入以下代码,要注意的是,entry这项配置是我们两个子项目的域名和端口,我们必须确保两个子项目运行在这两个端口上面,container就是我们的容器名,就是我们子应用挂载的节点,相当于Vue项目里面的app节点,activeRule就是我们的激活路径,根据路径来显示不同的子应用

    • 引入qiankun插件
    yarn add qiankun 或者 npm i qiankun -S
    • main.js配置
    1. import Vue from 'vue'
    2. import App from './App.vue'
    3. import router from './router'
    4. import ElementUI from 'element-ui'
    5. import 'element-ui/lib/theme-chalk/index.css';
    6. import less from 'less';
    7. import Vuex from 'vuex';
    8. // 引入乾坤
    9. import { registerMicroApps, start, initGlobalState } from 'qiankun';
    10. import store from './store/store'
    11. Vue.use(ElementUI)
    12. Vue.use(less)
    13. Vue.use(Vuex)
    14. Vue.config.productionTip = false
    15. let propsData = {
    16. sex: '女',
    17. age: 18,
    18. userName: '小莫'
    19. }
    20. const actions = initGlobalState(propsData);
    21. // 主项目项目监听和修改(在项目中任何需要监听的地方进行监听)
    22. actions.onGlobalStateChange((state, prev) => {
    23. //state变更后的状态,prev变更前的状态
    24. console.log("改变前的值", prev);
    25. console.log("改变后的值", state);
    26. });
    27. //将actions对象绑到Vue原型上,为了项目中其他地方方便使用
    28. Vue.prototype.$actions = actions
    29. const apps = [
    30. {
    31. name: 'vueApp1',//应用的名字
    32. entry: '//localhost:8081',//默认会加载这个html解析里面的js动态的执行(子应用必须支持跨域)fetch
    33. container: '#vue1',//容器名
    34. activeRule: '/main/vue',//激活的路径
    35. props: propsData,
    36. },
    37. {
    38. name: 'vueApp2',//应用的名字
    39. entry: '//localhost:8082',//默认会加载这个html解析里面的js动态的执行(子应用必须支持跨域)fetch
    40. container: '#vue2',//容器名
    41. activeRule: '/main/vue1',//激活的路径
    42. props: propsData,
    43. }
    44. ]
    45. registerMicroApps(apps);//注册应用
    46. start({
    47. prefetch: false//取消预加载
    48. });//开启
    49. new Vue({
    50. router,
    51. store,
    52. render: h => h(App),
    53. }).$mount('#app')
    • 配置完之后,这里我写了一个侧边栏引用其他子应用到基座中
      1. <!--
      2. * @Author: mowencong 2829621014@qq.com
      3. * @Date: 2022-08-26 16:51:38
      4. * @LastEditors: mowencong 2829621014@qq.com
      5. * @LastEditTime: 2022-08-29 10:11:09
      6. * @FilePath: \qiankun\qiankun-base\src\views\Main\index.vue
      7. * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
      8. -->
      9. <template>
      10. <div class="main-content">
      11. <el-container>
      12. <!-- 顶部 -->
      13. <el-header>
      14. <router-link to="/home">
      15. <img src="../../assets/logo.png" class="logo" />
      16. </router-link>
      17. </el-header>
      18. <el-container>
      19. <!-- 左侧导航 -->
      20. <el-aside>
      21. <el-menu
      22. default-active="1"
      23. class="el-menu-vertical-demo"
      24. background-color="#545c64"
      25. text-color="#fff"
      26. :router="true"
      27. >
      28. <!--基座中可以放自己的路由-->
      29. <el-menu-item index="/home">
      30. <i class="el-icon-menu"></i>
      31. <span slot="title">Home</span>
      32. </el-menu-item>
      33. <el-menu-item index="/about">
      34. <i class="el-icon-menu"></i>
      35. <span slot="title">About</span>
      36. </el-menu-item>
      37. <!--引用其他子应用-->
      38. <el-menu-item index="/main/vue">
      39. <i class="el-icon-document"></i>
      40. <span slot="title">qiankun-vue1</span>
      41. </el-menu-item>
      42. <el-menu-item index="/main/vue1">
      43. <i class="el-icon-setting"></i>
      44. <span slot="title">qiankun-vue2</span>
      45. </el-menu-item>
      46. </el-menu>
      47. </el-aside>
      48. <el-container>
      49. <!-- 内容 -->
      50. <el-main>
      51. <router-view></router-view>
      52. <!--这里就是子应用挂载的节点,跟main.js里面的配置一一对应-->
      53. <div id="vue1"></div>
      54. <div id="vue2"></div>
      55. </el-main>
      56. </el-container>
      57. </el-container>
      58. </el-container>
      59. </div>
      60. </template>
      61. <script>
      62. import { start } from "qiankun";
      63. export default {
      64. name: "Main",
      65. mounted() {
      66. if (!window.qiankunStarted) {
      67. window.qiankunStarted = true;
      68. start();
      69. }
      70. },
      71. };
      72. </script>
      73. <style lang='less' scoped>
      74. .main-content {
      75. height: 100%;
      76. & > .el-container {
      77. height: 100%;
      78. .el-header {
      79. background-color: #3f4044;
      80. display: flex;
      81. align-items: center;
      82. .logo {
      83. height: 40px;
      84. width: auto;
      85. cursor: pointer;
      86. }
      87. }
      88. & > .el-container {
      89. height: 100%;
      90. & > .el-aside {
      91. background-color: #d3dce6;
      92. width: 200px !important;
      93. .el-menu-vertical-demo {
      94. height: 100%;
      95. }
      96. }
      97. .el-container {
      98. .el-main {
      99. // background-color: #e9eef3;
      100. // color: #333;
      101. }
      102. }
      103. }
      104. }
      105. }
      106. </style>

      2、子应用qiankun-vue配置

    • main.js配置

    1. /*
    2. * @Author: mowencong 2829621014@qq.com
    3. * @Date: 2022-08-26 15:57:22
    4. * @LastEditors: mowencong 2829621014@qq.com
    5. * @LastEditTime: 2022-08-29 10:50:43
    6. * @FilePath: \qiankun\qiankun-vue\src\main.js
    7. * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
    8. */
    9. import Vue from 'vue'
    10. import App from './App.vue'
    11. import router from './router'
    12. import ElementUI from 'element-ui'
    13. import 'element-ui/lib/theme-chalk/index.css';
    14. Vue.use(ElementUI)
    15. Vue.config.productionTip = false
    16. let instance = null
    17. function render(props){
    18. instance = new Vue({
    19. router,
    20. render:h => h(App)
    21. }).$mount('#qkApp1');//这里是挂载到自己的html中,基座会拿到这个挂载后的html,将其插入进去
    22. //注意:这里#qkApp1index.html和app.vue的挂载点的id名要同步修改
    23. }
    24. if(window._POWERED_BY_QIANKUN_){//动态添加publicPath
    25. _webpack_public_path_ = window._INJECTED_PUBLIC_PATH_BY_QIANKUN_;
    26. }
    27. if(!window._POWERED_BY_QIANKUN_){//默认独立运行
    28. render();
    29. }
    30. //父应用加载子应用,子应用必须暴露三个接口:bootstrap,mount,unmount
    31. //子组件的协议就ok了
    32. export async function bootstrap(props){};
    33. export async function mount(props){
    34. //加了true之后,会自动调取前面这个回调方法,这样可以拿到主应用(基座)修改的值
    35. props.onGlobalStateChange((state,prev) => {
    36. console.log(state,prev);
    37. },true);
    38. Vue.prototype.$onGlobalStateChange = props.onGlobalStateChange;
    39. Vue.prototype.$setGlobalState = props.setGlobalState;
    40. console.log('是啥类型',Object.prototype.toString.call(props.onGlobalStateChange))
    41. render(props);
    42. }
    43. export async function unmount(props){
    44. instance.$destroy();
    45. }

    router.js的配置

    1. /*
    2. * @Author: mowencong 2829621014@qq.com
    3. * @Date: 2022-08-26 17:05:52
    4. * @LastEditors: mowencong 2829621014@qq.com
    5. * @LastEditTime: 2022-08-29 09:37:57
    6. * @FilePath: \qiankun\qiankun-vue\src\router\index.js
    7. * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
    8. */
    9. import Vue from 'vue'
    10. import VueRouter from 'vue-router'
    11. import Home from '../views/Home.vue'
    12. Vue.use(VueRouter);
    13. const routes = [
    14. {
    15. path: '/',
    16. redirect: '/main/vue/'
    17. },
    18. {
    19. path: '/main/vue/',
    20. name: 'Home',
    21. component: Home
    22. },
    23. {
    24. path: '/main/vue/about',
    25. name: 'About',
    26. component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
    27. }
    28. ]
    29. const router = new VueRouter({
    30. mode: 'history',
    31. base: window.__POWERED_BY_QIANKUN__ ? "/vue" : "/",//基础路径
    32. routes
    33. })
    34. export default router
    35. //注意:路由前缀必须设置,这个跟基座main.js的激活路径相关

    vue.config.js的配置

    1. /*
    2. * @Author: mowencong 2829621014@qq.com
    3. * @Date: 2022-08-26 15:57:22
    4. * @LastEditors: mowencong 2829621014@qq.com
    5. * @LastEditTime: 2022-08-29 10:08:14
    6. * @FilePath: \qiankun\qiankun-vue\vue.config.js
    7. * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
    8. */
    9. module.exports = {
    10. lintOnSave:false,//关闭eslint检测
    11. devServer:{
    12. port:8081,//这里的端口必须和父应用配置的子应用端口一致
    13. headers:{
    14. //因为qiankun内部请求都是fetch请求资源,所以子应用必须允许跨域
    15. 'Access-Control-Allow-Origin':'*',
    16. }
    17. },
    18. configureWebpack:{
    19. output:{
    20. //资源打包路径
    21. library:'vueApp1',
    22. libraryTarget:'umd'
    23. }
    24. }
    25. }

    3、注意点

    1)如何在主应用的某个路由页面加载微应用

    react + react-router技术栈的主应用:只需要让子应用的activeRule包含主应用的这个路由即可

    vue + vue-router技术栈的主应用

    例如:主应用需要在login页面登录,登录成功后跳转到main后台管理页面,在main管理页面下可以显示子应用

    修改主应用router.js

    1. //如果这个路由有其他子路由,需要另外注册一个路由,仍然使用这个子组件即可
    2. //本案例就是有子路由,所以需要才后面重新定义main页面的路由
    3. const routes = [
    4. {
    5. path:'/',
    6. name:'Login'
    7. component:Login
    8. },
    9. {
    10. path:'/main',
    11. name:'Main',
    12. component:Main,
    13. children:[
    14. {
    15. path:'/home',
    16. name:'Home',
    17. component:Home
    18. },
    19. {
    20. path: '/about',
    21. name: 'About',
    22. // route level code-splitting
    23. // this generates a separate chunk (about.[hash].js) for this route
    24. // which is lazy-loaded when the route is visited.
    25. component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
    26. }
    27. ]
    28. },
    29. {
    30. path: '/main/*',
    31. name: 'Main',
    32. component: Main,
    33. }
    34. ]

    修改主应用main.js 的文件

    1. // 子应用的 activeRule 需要包含主应用的这个路由 path
    2. const apps = [
    3. {
    4. name: 'vueApp', // 应用的名字
    5. entry: '//localhost:8081', // 默认会加载这个html 解析里面的js 动态的执行 (子应用必须支持跨域)fetch
    6. container: '#vue', // 容器名
    7. activeRule: '/main/vue', // 激活的路径
    8. props: { a: 1 }
    9. },
    10. {
    11. name: 'reactApp',
    12. entry: '//localhost:20000', // 默认会加载这个html 解析里面的js 动态的执行 (子应用必须支持跨域)fetch
    13. container: '#react',
    14. activeRule: '/main/react',
    15. }
    16. ]
    17. registerMicroApps(apps); // 注册应用

  • 相关阅读:
    查看请求服务的ip:netstat
    第一个 Go 程序,从 Hello World 开始
    Scala重要知识
    简历准备及面试技巧,你应该知道的一切
    编译器的作用和构成 (基础知识版)
    DCMTK findscu 查询信息
    Mysql索引、事务与存储引擎 (事务、MySQL 存储引擎)
    【无标题】
    uni-app_消息推送_华为厂商_unipush离线消息推送
    C# 发送邮件
  • 原文地址:https://blog.csdn.net/qq_37049128/article/details/126616824