您现在的位置是:首页 >学无止境 >qiankun在主应用、子应用均为vue3+vite+typescript中的使用网站首页学无止境

qiankun在主应用、子应用均为vue3+vite+typescript中的使用

m0_37653407 2024-09-14 00:01:02
简介qiankun在主应用、子应用均为vue3+vite+typescript中的使用

介绍

什么是微前端

微前端是一种架构风格,通过将一个单体应用程序拆分成多个小型独立应用程序来实现。每个小型应用程序都专注于一项特定功能,并且可以独立部署、扩展和维护。

为什么需要微前端

  • 复杂性管理

大型单体应用程序随着时间的推移变得越来越复杂,难以维护

  • 团队独立性

微前端允许团队根据其能力和需求独立工作,而不需要干扰其他团队

  • 技术多样性

使用不同的技术堆栈开发各个独立应用程序,而无需考虑整体应用程序的技术选型

核心价值

  • 技术栈无关

主框架不限制接入应用的技术栈,子应用具备完全自主权

  • 独立开发、独立部署

子应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新

  • 独立运行时

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

优点

  • 可扩展性

通过添加新应用程序来扩展系统而无需修改原始代码

  • 独立部署

每个应用程序都可以独立部署、减少了系统的停机时间

  • 更好的可用性和容错性

由于应用程序之间相互独立,因此如果一个应用程序发生故障,其他应用程序可以继续运行

缺点

  • 导致依赖项冗余,增加用户的流量负担

  • 团队自洽程度的增加,可能会破坏协作

qiankun ——一套完整的微前端解决方案

qiankun能很方便的将一个巨石应用改造成一个基于微前端框架的系统,并且不再需要去关注各种过程中的技术细节,做到真正的开箱即用和生产可用

特点

  • 简单

任意js框架均可使用,微应用接入像使用接入一个iframe系统一样简单,但实际不是iframe

  • 完备

几乎包含所有构建微前端系统时所需的基本能力,如样式隔离、js沙箱、预加载等

  • 生产可用

已在蚂蚁内外经受过足够大量的线上系统的考验及打磨,健壮性值得信赖

使用方法

主应用(vue3+vite+TypeScript)

  1. 创建项目

    npm init vue@latest
    
  2. 安装qiankun

    yarn add qiankun
    
  3. main.ts注册微应用

    import { registerMicroApps, addGlobalUncaughtErrorHandler, start } from 'qiankun';
    // 注册子应用
    registerMicroApps([
      {
        // 子应用名称,name值必须与子应用vite.config.ts文件中plugins属性qiankun的第一个参数值一致
        name: 'subApp',
        // 默认会加载这个路径下的html,解析里面的js
        entry: '//localhost:5174',
        // 加载的容器(微应用会显示到这个容器里面,一定要保证主应用中有这个容器)
        container: '#subAppContainerVue3',  // 和app.vue配置的节点一致
        // 匹配的路由
        activeRule: '/junminronghe', // 访问:http://localhost:5174/juminronghe
        props: {
            mag: '我是主应用main', // 主应用向微应用传递参数
            domain: 'http://localhost:5174',
        },
      }
      // 再有其他子应用,同理
    ]);
    // 启动 qiankun
    start({
        prefetch:'all', // 预加载
        sandbox: {
            experimentalStyleIsolation: true, //   开启沙箱模式,实验性方案
        },
    });
    
    // 添加全局异常捕获
    addGlobalUncaughtErrorHandler((handler) => {
        console.log("异常捕获", handler);
    });
    
  4. app.vue创建挂载节点,如再有其他子应用,也是同理

    <div id="subAppContainerVue3"></div>
    
  5. 访问子应用

    <script setup lang="ts">
    import { useRouter } from 'vue-router'
    
    const router = useRouter();
    function goToFunc() {
      router.push('/junminronghe');
    }
    </script>
    
    <template>
      <main>
        <div @click="goToFunc">子应用</div>
      </main>
    </template>
    

子应用(vue3+vite+TypeScript)

  1. 安装vite-plugin-qiankunqiankun官方不支持vite)需要安装插件

    yarn add vite-plugin-qiankun
    
  2. vite.config.ts

    import { fileURLToPath, URL } from 'node:url'
    import vue from '@vitejs/plugin-vue'
    import qiankun from 'vite-plugin-qiankun'
    
    export default defineConfig({
      plugins: [
        vue(),
      	// qiankun的第一个参数必须与主应用在main.ts中registerMicroApps的name值一致
        qiankun('vue3', {
          useDevMode: true
        })
      ],
      server:{
        headers: {
          'Access-Control-Allow-Origin': '*', // 主应用获取子应用时跨域响应头
        },
      },
      resolve: {
        alias: {
          '@': fileURLToPath(new URL('./src', import.meta.url))
        }
      }
    })
    
  3. main.ts

    import { createApp } from 'vue'
    import { renderWithQiankun, qiankunWindow, QiankunProps } from 'vite-plugin-qiankun/dist/helper'
    import App from './App.vue'
    import router from './router'
     
    const app = createApp(App)
     
    app.use(router)
     
    // app.mount('#app')
    // renderWithQiankun: 为子应用导出一些生命周期函数 供主应用在特殊的时机调用
    // qiankunWindow: qiankunWindow.POWERED_BY_QIANKUN 可判断是否在qiankun环境下
    const initQianKun = () => {
        renderWithQiankun({
            // bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap
            // 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等
            bootstrap() {
                console.log('bootstrap');
            },
            // 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法,也可以接受主应用传来的参数
            mount(_props: any) {
                console.log('mount', _props);
                render(_props.container)
            },
            // 应用每次 切出/卸载 会调用的unmount方法,通常在这里我们会卸载微应用的应用实例
            unmount(_props: any) {
                console.log('unmount', _props);
            },
            update: function (props: QiankunProps): void | Promise<void> {
                console.log('update');
            }
        });
    }
     
    const render = (container) => {
        // 如果是在主应用的环境下就挂载主应用的节点,否则挂载到本地
        const appDom = container ? container : "#app"
        app.mount(appDom)
    }
    // 判断是否为乾坤环境,否则会报错[qiankun]: Target container with #subAppContainerVue3 not existed while subAppVue3 mounting!
    qiankunWindow.__POWERED_BY_QIANKUN__ ? initQianKun() : render(null)
    
  4. router——》index.ts

    import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
    import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper'
    
    const routes: RouteRecordRaw[] = [
      {
        path: '/',
        name: 'home',
        component: () => import('../views/home/index.vue')
      },
      // 文章详情
      {
        path: '/content',
        name: 'content',
        component: () => import('../views/content/index.vue')
      },
    ];
    
    const router = createRouter({
      history: createWebHistory(
        // 在乾坤环境下时,将所有的路由地址前加上'/junminronghe',否则将跳转至主应用的相应页面(eg:跳转至这里的文章详情页面,如果不加'/junminronghe'的话,将跳转至主应用的'/content'页面,如果主应用没有这个页面,将跳转至404页面)
        qiankunWindow.__POWERED_BY_QIANKUN__ ? '/junminronghe' : '/'
      ),
      routes
    })
    export default router
    
    

1、子应用的本地资源(图片等)显示404

解决方法:

在子应用的vite.config.ts文件的server对象中配置origin属性,配置为子应用的链接地址

server:{
  port: 5174,
  // origin:用于定义开发调试阶段生成资产的 origin
  // 子应用的地址,用于解决主应用中子应用出现静态地址404问题,控制台报错显示该资源在主应用路劲下
  origin: 'http://localhost:5174',
}

2、子应用的接口404

在创建axios实例时,配置baseURL

子应用:

// 创建 axios 实例
const instance = axios.create({
  // API 请求的默认前缀
  // "/junminrongheApi"要跟主应用,vite.config.ts中,代理的地址一致,给所有的接口请求地址前加“/junminrongheApi"字符串,表明是junminronghe子应用的接口
  baseURL: qiankunWindow.__POWERED_BY_QIANKUN__ ? '/junminrongheApi' : '',
  timeout: 60 * 1000, //设置超时
  headers: {
    "Content-Type": "application/json;charset=UTF-8;",
    "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
    ".AspNetCore.Culture": "c=zh-Hans|uic=zh-Hans",
  },
});

主应用:vite.config.ts

export default defineConfig({
  plugins: [vue()],
  server: {
    proxy: {
      // "/junminrongheApi"要跟子应用,service——》index.ts中,baseURL的地址一致(即axios的baseURL地址一致),
      // 将junminronghe子应用的接口地址代理到其自己的地址(即target与主应用main.ts中registerMicroApps的entry值,保持一致)即可
      "/junminrongheApi": {
        target: 'http://localhost:5174', // 后台接口
        changeOrigin: true,
        secure: false, // 如果是https接口,需要配置这个参数
        // ws: true, //websocket支持
        rewrite: (path) => path.replace(/^/junminrongheApi/, ""),
      },
    }
  },
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  }
})

3、子应用的线上图片地址显示404(一般不会有这个问题)

注:这个是因为这里的图片地址不是常规的地址,而是像这样的地址:sysFileInfo/preview?id=1666025519278534658,所以才会出现这个问题,这个问题是特例,正常的图片地址(eg:https://vitejs.cn/vite3-cn/logo.svg)就不会有这个问题

  • 普通的线上图片加载

解决方法:

在所有的图片地址前,手动拼接一个请求地址,可以封装一个图片组件,统一管理

eg:

<n-image :object-fit="props.objectFit" :src="previewUrl + props.url" />
  • 富文本中的图片加载

富文本中的图片,在加载时,图片地址(sysFileInfo/preview?id=1666025519278534658)前会被拼上主应用的地址及前面配置的子应用路由拼接的'/junminronghe'(目前不知道是为啥)

解决方法:

主应用vite.config.ts

export default defineConfig({
  plugins: [vue()],
  server: {
    proxy: {
      // "/junminrongheApi"要跟子应用,service——》index.ts中,baseURL的地址一致(即axios的baseURL地址一致),
      // 将junminronghe子应用的接口地址代理到其自己的地址(即target与主应用main.ts中registerMicroApps的entry值,保持一致)即可
      "/junminrongheApi": {
        target: 'http://localhost:5174', // 后台接口
        changeOrigin: true,
        secure: false, // 如果是https接口,需要配置这个参数
        // ws: true, //websocket支持
        rewrite: (path) => path.replace(/^/junminrongheApi/, ""),
      },
      // 加这个代理是因为像子应用中如果有富文本,并且富文本中有图片的话,请求的图片会在主应用的地址后面拼上子应用的路由地址(即这里的junminronghe)后再是图片的地址,
      // 当然是因为这里的子应用的图片地址不是常规的地址,而是像这样的地址:sysFileInfo/preview?id=1666025519278534658,才会有这样的问题
      "/junminronghe": {
        target: 'http://localhost:5174', // 后台接口
        changeOrigin: true,
        secure: false, // 如果是https接口,需要配置这个参数
        // ws: true, //websocket支持
        rewrite: (path) => path.replace(/^/junminronghe/, ""),
      },
    }
  },
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  }
})
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。