vue3+vite改造子系统

更新时间: 2023-03-07 13:36:47

公司的新项目需要能够单独运行,也能够作为子系统接入公司的主系统中,但是vue2我写得有点烦了,所以新项目决定采用vue3+ts+vite+pinia+element-plus。

# 遇到问题

改造成子系统的过程中遇到了问题

  • 开发模式下,如果我们使用vite来构建vue3子应用,基于vite的构建机制,会在子应用的html的入口文件script标签携带type=module。而我们知道qiankun父应用直接引入子应用,本质上是将html作为入口文件,并通过import-html-entry这个库去加载子应用所需要的资源列表js,css,然后通过eval直接执行,而基于vite构建的js中import、export并没有被转码,会导致直接报错(不允许在非type=module的script里面使用import)

  • 生产模式下,因为没有webpack中支持运行时publicPath,也就是__webpack_public_path__,换句话说就是vite不支持运行时publicPath,其主要作用是用来解决微应用动态载入的脚本,样式,图片等地址不正确的问题。

# 改造子应用

  1. 安装vite-plugin-qiankun插件
npm install vite-plugin-qiankun
1
  1. 修改 vite.config.ts
import { defineConfig, loadEnv  } from 'vite'
import vue from '@vitejs/plugin-vue'
import qiankun from 'vite-plugin-qiankun'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver,AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
import path from 'path'
import {name} from './package.json'

// useDevMode 开启时与热更新插件冲突
const useDevMode = true // 如果是在主应用中加载子应用vite,必须打开这个,否则vite加载不成功, 单独运行没影响

export default defineConfig(({ mode }) => {
  const root = process.cwd() //  process.cwd()返回当前工作目录
  const env = loadEnv(mode, root)

  let config = {
    base: env.VITE_BASE_API,
    plugins: [
      vue(),
      //下面这俩插件是注册element-plus的
      AutoImport({
        resolvers: [ElementPlusResolver()],
      }),
      Components({
        resolvers: [ElementPlusResolver()],
      }),
        // 微应用名字,与主应用注册的微应用名字保持一致
        qiankun(name, { useDevMode })
    ],
    resolve: {
      alias: {
        '@': path.resolve(__dirname,'./src')
      }
    },
    output: {
      // 把子应用打包成 umd 库格式
      library: `${name}-[name]`,
      libraryTarget: 'umd',
      jsonpFunction: `webpackJsonp_${name}`
    }
  }
  return config
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  1. 修改main.ts
import { createApp,App } from 'vue'
import MyApp from './App.vue'
import {initRouter} from './router'
import { initStore } from './store'
import {initPromission} from "@/permission";
import {initComponent} from '@/components/publicComponents'
import {
    renderWithQiankun,
    qiankunWindow
} from 'vite-plugin-qiankun/dist/helper'

let instance:App<Element>|null = null

declare global {
    interface Window {
        __POWERED_BY_QIANKUN__: any
        __INJECTED_PUBLIC_PATH_BY_QIANKUN__: any
    }
}

function render(props = {}) {
    instance = createApp(MyApp)
    initRouter(instance)
    initPromission(instance)
    initComponent(instance)
    // 初始化vuex
    initStore(instance)
    instance.mount('#app')
    if (qiankunWindow.__POWERED_BY_QIANKUN__) {
        console.log('我正在作为子应用运行')
    }
}

renderWithQiankun({
    mount(props) {
        console.log('viteapp mount')
        render(props)
    },
    bootstrap() {
        console.log('bootstrap')
    },
    update(){
        console.log('哎呀更新了')
    },
    unmount(props) {
        console.log('vite被卸载了')
        instance.unmount()
        instance = null
    }
})

if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
    render({})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

需要注意的是,判断是否是作为子应用运行,使用的是qiankunWindow.__POWERED_BY_QIANKUN__,生命周期mountbootstrapupdate,unmount 等通过插件函数renderWithQiankun在其中暴露完成。其他配置与基于webpack构建的子应用相同。

  1. 改造router.ts
import { App } from 'vue'
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import {qiankunWindow} from "vite-plugin-qiankun/dist/helper";
import {name} from '../../package.json'

let history = createWebHashHistory()

// 判断是 qiankun 环境则增加路由前缀
let prefix = ''
if(qiankunWindow.__POWERED_BY_QIANKUN__){
    prefix = '/'+name
}

let routes:RouteRecordRaw[] = [
    {
        path:prefix + '/login',
        name:'登录',
        component: () => import('@/views/login/login.vue')
    },
    {
        path:prefix + '/homepage',
        name:'首页',
        component: () => import('@/views/index/index.vue')
    }
]
export const router = createRouter({
    history,
    routes
})
export const initRouter = (app: App<Element>)=>{
    app.use(router)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

暂时就只踩到这些坑了,项目还没写完,到这里已经可以正常的在主系统里接入了,后面遇到新坑再来更新~