基于vue2基座接入vue3+vite+ts+arcoDesign进行vue3子应用改造详细步骤真实项目实战保姆教学附带nginx配置

前言:

本篇文章介绍如何将以vue2为基座,使用qiankun框架独立技术栈,可复用性进行vue3+vite+ts+arcoDesign的子项目改造并接入,子应用可根据基座传递过来基础路由进行改变,保证主子应用访问不会报404错误。

整体结构:

vue2+ruoyi+webpack作为基座,vue+vite+ts+ArcoDesignUI 作为子应用接入,尽量把基座保持整洁,保证基座不保函业务,只分享token及动态菜单。

接入后的效果:

页面响应式还没做好,整体效果可以显现,我将vue2版本的左侧菜单改成了acro.design版本的左侧菜单样式,样式尽量保持一致!
基于vue2基座接入vue3+vite+ts+arcoDesign进行vue3子应用改造详细步骤真实项目实战保姆教学附带nginx配置

vue2基座整改,注册子应用,配置子应用路由

  1. 安装qiankun
npm i qiankun -S 
  1. 设置需要将子应用的页面嵌入到主应用的某个div中(子应用在主应用上的渲染出口)<div id="subapp-container">
    基于vue2基座接入vue3+vite+ts+arcoDesign进行vue3子应用改造详细步骤真实项目实战保姆教学附带nginx配置

在主应用(基座)中注册子应用

  1. 引入registerMicroApps, start, setDefaultMountApp
import { registerMicroApps, start, setDefaultMountApp } from 'qiankun';
  1. 注册子应用

import Vue from 'vue'

import Cookies from 'js-cookie'

import Element from 'element-ui'
import './assets/styles/element-variables.scss'
import { registerMicroApps, start, setDefaultMountApp } from 'qiankun';

// import 'ant-design-vue/dist/antd.css';

import "ant-design-vue/dist/antd.less"
import 'default-passive-events'
import '@/assets/styles/index.scss' // global css
import '@/assets/styles/ruoyi.scss' // ruoyi css
import App from './App'
import store from './store'
import router from './router'
import directive from './directive' // directive
import plugins from './plugins' // plugins
import { download } from '@/utils/request'

import './assets/icons' // icon
import './permission' // permission control
import { getDicts } from "@/api/system/dict/data";
import { getConfigKey } from "@/api/system/config";

// import {userApi} from "hskCommApi"
// console.log("asdasd",userApi.login)
import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi";
// 分页组件
import Pagination from "@/components/Pagination";
// 自定义表格工具组件
import RightToolbar from "@/components/RightToolbar"
// 富文本组件
import Editor from "@/components/Editor"
// 文件上传组件
import FileUpload from "@/components/FileUpload"
// 图片上传组件
import ImageUpload from "@/components/ImageUpload"
// 图片预览组件
import ImagePreview from "@/components/ImagePreview"
// 字典标签组件
import DictTag from '@/components/DictTag'
// 头部标签组件
import VueMeta from 'vue-meta'
// 字典数据组件
import DictData from '@/components/DictData'
//局部使用antDesign-vue中的tree组件
import { Tree } from 'ant-design-vue';
import { Table } from 'ant-design-vue';
import { Icon } from 'ant-design-vue';
// import a from "hskCommApi"
Vue.config.productionTip = false;
// 全局方法挂载
Vue.prototype.getDicts = getDicts
Vue.prototype.getConfigKey = getConfigKey
Vue.prototype.parseTime = parseTime
Vue.prototype.resetForm = resetForm
Vue.prototype.addDateRange = addDateRange
Vue.prototype.selectDictLabel = selectDictLabel
Vue.prototype.selectDictLabels = selectDictLabels
Vue.prototype.download = download
Vue.prototype.handleTree = handleTree


// 全局组件挂载
Vue.component('DictTag', DictTag)
Vue.component('Pagination', Pagination)
Vue.component('RightToolbar', RightToolbar)
Vue.component('Editor', Editor)
Vue.component('FileUpload', FileUpload)
Vue.component('ImageUpload', ImageUpload)
Vue.component('ImagePreview', ImagePreview)
Vue.component('ATree', Tree)
Vue.component('ATable', Table)
Vue.component('AIcon', Icon)

Vue.use(directive)
Vue.use(plugins)
Vue.use(VueMeta)
DictData.install()

/**
 * If you don't want to use mock-server
 * you want to use MockJs for mock api
 * you can execute: mockXHR()
 *
 * Currently MockJs will be used in the production environment,
 * please remove it before going online! ! !
 */

Vue.use(Element, {
  size: Cookies.get('size') || 'medium' // set element-ui default size
})
// 1. 注册微应用 
registerMicroApps([
  {
    name: 'vinsion-module',  // 微应用package.json的name字段
    entry: '//192.168.80.15:8010/vinsion-module/', // 微应用访问地址,默认加载这个html页面并解析其中的js动态执行
    container: '#subapp-container', // 子应用渲染的出口
    // return location.pathname.includes('/vite-vue3-app2') 
    activeRule: '/hsk-admin/vinsion-module',// 激活路径,微应用路由
    sandbox: {
      // strictStyleIsolation: true, // 开启样式隔离
    },
    props: { sharedStore: store, baseName: '/hsk-admin/system-module' }
  },
])
// 判断subapp-container是否已加载,如果未加载就延迟
function ensureContainerAndStartMicroApps() {
  if (document.getElementById('subapp-container')) {
    // 容器存在,可以注册微应用并启动
    // registerMicroApps([...]); // 注册微应用的代码
    setDefaultMountApp('/'); // 默认打开的子应用
    start({
      sandbox: {
        // strictStyleIsolation: true,
        experimentalStyleIsolation: true
      }
    }); // 启动 qiankun
  } else {
    // 容器尚不存在,稍后重试
    setTimeout(ensureContainerAndStartMicroApps, 100); // 100毫秒后再次尝试
  }
}

// 确保 DOMContentLoaded 事件触发后再执行
document.addEventListener('DOMContentLoaded', ensureContainerAndStartMicroApps);
Vue.config.productionTip = false

new Vue({
  el: '#app',
  router,
  store,
  render: h => h(App)
})
  1. 配置子应用的路由,根据activeRule进行判断当激活的子应用
  {
    path: '/vinsion-module',
    component: Layout,
    name: 'vinsion-module',
    meta: { title: '生产单位', icon: 'tool' },
    children: [
      {
        path: 'visualization/multi-dimension-data-analysis',
        name: 'MultiDimensionDataAnalysis',
        meta: { title: '生产单位', noCache: true, icon: 'tool' }
      },
    ]
  },

这样基座就算改造完成啦

vue3+vite+ts+acro.Design进行子应用改造

因为qiankun目前不支持vite项目,vite项目需要安装社区依赖vite-plugin-qiankun

  1. 安装viteqiankun1社区依赖vite-plugin-qiankun
npm install vite-plugin-qiankun

基于vue2基座接入vue3+vite+ts+arcoDesign进行vue3子应用改造详细步骤
2. 修改package.json文件,和主应用注册子应用时的name值相同
基于vue2基座接入vue3+vite+ts+arcoDesign进行vue3子应用改造详细步骤真实项目实战保姆教学附带nginx配置
3. 修改vite.config.dev.ts或者vite.config.ts文件

import { mergeConfig } from 'vite';
import { fileURLToPath, URL } from 'node:url'
import eslint from 'vite-plugin-eslint';
import qiankun from 'vite-plugin-qiankun';
// import vue from "@vitejs/plugin-vue";
import baseConfig from './vite.config.base';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const packName = require('../package.json').name 

export default mergeConfig(
  {
    base: "/vinsion-module",  // 子应用基础地址
    mode: 'development',
    server: {
      open: true,
      cors: true,
      host: '0.0.0.0',
      fs: {
        strict: true,
      },
      https: false,
      port:"8010",
      headers: {
        "Access-Control-Allow-Origin": "*", // 避免跨域
      },
    },
    resolve: {
      alias: {
        '@': fileURLToPath(new URL('./src', import.meta.url))
      }
    },
    plugins: [
      // vue(),
      qiankun(`vinsion-module`,{  // 子应用的name值
        useDevMode: true
      }),
      eslint({
        cache: false,
        include: ['src/**/*.ts', 'src/**/*.tsx', 'src/**/*.vue'],
        exclude: ['node_modules'],
      }),
    ],
  },
  baseConfig
);

  1. 添加public-path.tsmain.ts引入
    public-path.ts的作用:public-path.ts 文件的作用就是用来设置子应用的静态资源文件的基础路径。在这个文件中,通过判断全局变量 __POWERED_BY_QIANKUN__ 是否存在,来确定当前应用是否运行在 qiankun 的微前端环境中。如果是,则通过 __INJECTED_PUBLIC_PATH_BY_QIANKUN__ 这个全局变量获取 qiankun 注入的基础路径,然后使用 __webpack_public_path__ 动态设置静态资源文件的基础路径。
    基于vue2基座接入vue3+vite+ts+arcoDesign进行vue3子应用改造详细步骤真实项目实战保姆教学附带nginx配置
    代码:
    /* eslint-disable camelcase */
    /* eslint-disable no-underscore-dangle */
    // src/public-path.ts
    if ((window as any).__POWERED_BY_QIANKUN__) {
      const __webpack_public_path__ = (window as any).__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
    }
    

main.ts引入:

import './public-path'
  1. 改造main.ts文件
/* eslint-disable no-underscore-dangle */
import './public-path'
import { createApp } from 'vue';
import ArcoVue from '@arco-design/web-vue';
import ArcoVueIcon from '@arco-design/web-vue/es/icon';
import globalComponents from '@/components';
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper';
import router from './router';
import store from './store';
import i18n from './locale';
import directive from './directive';
import './mock';
import App from './App.vue';
import '@/assets/style/global.less';
import '@/api/interceptor';

// 第五版本

let app: any;
// 判断是否在qiankun环境下
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
  app = createApp(App);
  app.use(ArcoVue, {});
  app.use(ArcoVueIcon);
  app.use(router);
  app.use(store);
  app.use(i18n);
  app.use(globalComponents);
  app.use(directive);
  app.mount('#app');
} else {
  renderWithQiankun({
    // qiankun的生命周期,挂载
    mount(props) {
      app = createApp(App);
      // 传递的值可以获取到了
      app.use(ArcoVue, {});
      app.use(ArcoVueIcon);
      app.use(router);
      app.use(store);
      app.use(i18n);
      app.use(globalComponents);
      app.use(directive);
      app.mount(
        props.container
          ? props.container.querySelector("#app")
          : document.getElementById("app")
      );
    },
    // 应用加载
    bootstrap() {
      console.log("--bootstrap");
    },
    // 修改
    update(props) {
      console.log("--update", props);
    },
    // 销毁
    unmount() {
      console.log("--unmount");
      app?.unmount();
    },
  });
}
  1. router/index.ts文件修改,注意 一定要是history模式,vue3只能用createWebHistory,根据qiankun环境来使用路由路径
/* eslint-disable no-underscore-dangle */
import { createRouter, createWebHistory } from 'vue-router';
import NProgress from 'nprogress'; // progress bar
import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper'
import 'nprogress/nprogress.css';

import { appRoutes } from './routes';
import { REDIRECT_MAIN, NOT_FOUND_ROUTE } from './routes/base';
import createRouteGuard from './guard';

NProgress.configure({ showSpinner: false }); // NProgress Configuration

console.log("router/index.ts",qiankunWindow.__POWERED_BY_QIANKUN__)

const router = createRouter({
  history: createWebHistory(qiankunWindow.__POWERED_BY_QIANKUN__ ? '/hsk-admin/vinsion-module' :'/vinsion-module'),
  routes: [
    {
      path: '/',
      redirect: 'login',
    },
    {
      path: '/login',
      name: 'login',
      component: () => import('@/views/login/index.vue'),
      meta: {
        requiresAuth: false,
      },
    },
    ...appRoutes,
    REDIRECT_MAIN,
    NOT_FOUND_ROUTE,
  ],
  scrollBehavior() {
    return { top: 0 };
  },
});

createRouteGuard(router);

export default router;

  1. 根据当前是否是子应用进行DOM的显示和不显示,当把子应用渲染到父组件中的时候,相对的菜单,头部标签栏也会被渲染过去,这时候就需要根据当前是否子应用判断是否要显示菜单,标签栏,头部了,下面是个简单的例子:根据qiankunWindow.__POWERED_BY_QIANKUN__进行判断是否显示。
<template>
  <a-breadcrumb v-if="!qiankunWindow.__POWERED_BY_QIANKUN__" class="container-breadcrumb">
    <a-breadcrumb-item>
      <icon-apps />
    </a-breadcrumb-item>
    <a-breadcrumb-item v-for="item in items" :key="item">
      {{ $t(item) }}
    </a-breadcrumb-item>
  </a-breadcrumb>
</template>

<script lang="ts" setup>
import { PropType } from 'vue';
import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper';

defineProps({
  items: {
    type: Array as PropType<string[]>,
    default() {
      return [];
    },
  },
});

</script>

<style scoped lang="less">
.container-breadcrumb {
  // margin: 16px 0;
  background: #fff;
  padding: 16px 15px;
  box-shadow: 0px 1px 10px 0px #0000001a;
  z-index: 9;
  :deep(.arco-breadcrumb-item) {
    color: rgb(var(--gray-6));
    &:last-child {
      color: rgb(var(--gray-8));
    }
  }
}
</style>

结语:qiankun虽然暂时不支持vue3+vite+ts项目,一步一个脚印,将qiankun理解透彻,冷静思考,不断尝试,镇定梳理,总会得到解决办法,下满是我经过了五次尝试,第五次配置成功。下面四种进行配置的时候,都会有些许瑕疵,果断舍去。
基于vue2基座接入vue3+vite+ts+arcoDesign进行vue3子应用改造详细步骤
最终效果:
基于vue2基座接入vue3+vite+ts+arcoDesign进行vue3子应用改造详细步骤真实项目实战保姆教学附带nginx配置

存在问题

当基座的baseURL被修改后,响应的子应用的也会被修改,比如vision-mode/business-module改成vision-web/business-module-vue3,这时候我们就需要通过基座的props方法,将基座的路径传递给子应用,子应用在app.mount之前进行基础路径修改。

要求效果:

修改前路径地址:
基于vue2基座接入vue3+vite+ts+arcoDesign进行vue3子应用改造详细步骤真实项目实战保姆教学附带nginx配置
修改后路径地址:
基于vue2基座接入vue3+vite+ts+arcoDesign进行vue3子应用改造详细步骤真实项目实战保姆教学附带nginx配置

整改步骤

一、修改基座router/index.js代码

export default new Router({
  mode: 'history', // 去掉url中的#
  base: 'vision-web',
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRoutes
})

二、修改基座main.js代码,将vision-mode/business-module改为/vision-web/business-module-vue3,通过通过props/vision-web/business-module-vue3传递给子应用,在子应用app.mount之前修改路由路径。

{
    name: 'business-module-vue3',  // 微应用package.json的name字段
    entry: '//192.168.80.15:8010/business-module-vue3/', // 微应用访问地址,默认加载这个html页面并解析其中的js动态执行
    container: '#subapp-container', // 子应用渲染的出口
    // return location.pathname.includes('/vite-vue3-app2') 
    activeRule: '/vision-web/business-module-vue3',// 激活路径,微应用路由
    sandbox: {
     strictStyleIsolation: true, // 开启样式隔离
    },
    props: { sharedStore: store, baseName: '/vision-web/business-module-vue3' }
  },

三、根据基座传递过来的baseNamemain.ts修改子应用路由地址,子应用所有代码如下:

/* eslint-disable no-underscore-dangle */
import './public-path'
import { createApp } from 'vue';
import ArcoVue from '@arco-design/web-vue';
import ArcoVueIcon from '@arco-design/web-vue/es/icon';
import globalComponents from '@/components';
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper';
import { createRouter, createWebHistory } from 'vue-router';
import router from './router';

import { appRoutes } from "./router/routes"
import { REDIRECT_MAIN, NOT_FOUND_ROUTE } from "./router/routes/base";
import store from './store';
import i18n from './locale';
import { setToken } from './utils/auth';
import directive from './directive';
import './mock';
import App from './App.vue';
import '@/assets/style/global.less';
import '@/api/interceptor';

// 第五版本

let app: any;
// 判断是否在qiankun环境下
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
  app = createApp(App);
  app.use(ArcoVue, {});
  app.use(ArcoVueIcon);
  app.use(router);
  app.use(store);
  app.use(i18n);
  app.use(globalComponents);
  app.use(directive);
  const token = window.name
  if (token) {
    setToken(token)
    router.push('/dashboard')
  }
  app.mount('#app');
} else {
  renderWithQiankun({
    // qiankun的生命周期,挂载
    mount(props) {
      console.log("~~~~~", props.baseName)
      // 创建子应用独立的路由配置
      const subAppRouter = createRouter({
        history: createWebHistory(props.baseName), // 使用父应用传递的 baseName 创建 history
        routes: [{
          path: '/',
          redirect: 'login',
        },
        {
          path: '/login',
          name: 'login',
          component: () => import('@/views/login/index.vue'),
          meta: {
            requiresAuth: false,
          },
        },
        ...appRoutes,
          REDIRECT_MAIN,
          NOT_FOUND_ROUTE,], // 这里可以添加你的子应用路由配置
      });
      app = createApp(App);
      // 传递的值可以获取到了
      app.use(ArcoVue, {});
      app.use(ArcoVueIcon);
      // app.use(router);
      app.use(subAppRouter)
      app.use(store);
      app.use(i18n);
      app.use(globalComponents);
      app.use(directive);
      const token = window.name
      if (token) {
        setToken(token)
        router.push('/dashboard')
      }
      app.mount(
        props.container
          ? props.container.querySelector("#app")
          : document.getElementById("app")
      );
    },
    // 应用加载
    bootstrap() {
      console.log("--bootstrap");
    },
    // 修改
    update(props) {
      console.log("--update", props);
    },
    // 销毁
    unmount() {
      console.log("--unmount");
      app?.unmount();
    },
  });
}

运行基座,就可以达到想要的效果了。
基于vue2基座接入vue3+vite+ts+arcoDesign进行vue3子应用改造详细步骤真实项目实战保姆教学附带nginx配置

nginx代理问题

下面是vue项目中的default.conf文件

server {
    listen       80;
    
    #server_name  nginx;

    #root /usr/share/nginx/html;

    location ^~/websocket/ {
        proxy_pass http://xxx.xxx.xxx.xxx:xxx;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_connect_timeout   5;
        proxy_read_timeout      90s;
        proxy_send_timeout      10s;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_set_header   X-Real-IP         $remote_addr;
    }
    
    location ^~/api/visioncloud/ {
        proxy_pass http://xxx.xxx.xxx.xxx:xxx/;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_set_header   X-Real-IP         $remote_addr;
    }
    
    location ^~/api/ {
        proxy_pass http://xxx.xxx.xxx.xxx:xxx/;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_set_header   X-Real-IP         $remote_addr;
    }
    
    location /vision-web {
      alias	 /usr/share/nginx/html/hsk-admin;
      index  index.html;
      try_files $uri $uri/ /hsk-admin/index.html;
    }
    
    location /hsk-admin {
      alias	 /usr/share/nginx/html/hsk-admin;
      index  index.html;
      try_files $uri $uri/ /hsk-admin/index.html;
    }
    
    location /ai-system-web {
      alias	 /usr/share/nginx/html/ai-system-web/dev/h5;
      index  index.html;
      try_files $uri $uri/ /ai-system-web/dev/h5/index.html;
    }
    
    location /open-platform-web {
      alias	 /usr/share/nginx/html/open-platform-web;
      index  index.html;
      try_files $uri $uri/ /open-platform-web/index.html;
    }
    
    location /ai-blemish-screen {
      alias	 /usr/share/nginx/html/ai-blemish-screen;
      index  index.html;
      try_files $uri $uri/ /ai-blemish-screen/index.html;
    }
    
    
    location /business-module-vue2 {
      add_header Access-Control-Allow-Origin *;
  		add_header Access-Control-Allow-Methods *;
  		add_header Access-Control-Allow-Headers *;
      alias	 /usr/share/nginx/html/business-module-vue2;
      index  index.html;
      try_files $uri $uri/ /business-module-vue2/index.html;
    }
    
    location /business-module-vue3 {
      add_header Access-Control-Allow-Origin *;
  		add_header Access-Control-Allow-Methods *;
  		add_header Access-Control-Allow-Headers *;
      alias	 /usr/share/nginx/html/business-module-vue3;
      index  index.html;
      try_files $uri $uri/ /business-module-vue3/index.html;
    }
    
    location /system-module-vue2 {
      add_header Access-Control-Allow-Origin *;
  		add_header Access-Control-Allow-Methods *;
  		add_header Access-Control-Allow-Headers *;
      alias	 /usr/share/nginx/html/system-module-vue2;
      index  index.html;
      try_files $uri $uri/ /system-module-vue2/index.html;
    }
    
    location /system-module-vue3 {
      add_header Access-Control-Allow-Origin *;
  		add_header Access-Control-Allow-Methods *;
  		add_header Access-Control-Allow-Headers *;
      alias	 /usr/share/nginx/html/system-module-vue3;
      index  index.html;
      try_files $uri $uri/ /system-module-vue3/index.html;
    }
    
    location /park-model-web {
      add_header Access-Control-Allow-Origin *;
  		add_header Access-Control-Allow-Methods *;
  		add_header Access-Control-Allow-Headers *;
      alias	 /usr/share/nginx/html/park-model-web;
      index  index.html;
      try_files $uri $uri/ /park-model-web/index.html;
    }
    
    location /prodectline-model-web {
      add_header Access-Control-Allow-Origin *;
  		add_header Access-Control-Allow-Methods *;
  		add_header Access-Control-Allow-Headers *;
      alias	 /usr/share/nginx/html/prodectline-model-web;
      index  index.html;
      try_files $uri $uri/ /prodectline-model-web/index.html;
    }
    
    #location @router {
        # 非静态资源,直接定向index.html
    #    rewrite ^.*$   /aps-admin/index.html  last;
    #}


    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
    #location /nacos {
    #    proxy_pass http://nacosList;
    #}

}

vue2基座配合vue3微应用完成~ 如果对你有帮助,给个小赞

  • 21
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值