qiankun + Vue + React18 搭建微前端

qiankun + Vue + React18 搭建微前端

本章节基于 Vue + qianakun 的方式搭建的微前端,微前端有主应用与微应用构成。

主应用: Vue3 + Vite

微应用:

  1. Vue2 + Webpack
  2. Vue3 + Vite
  3. React18 + webpack
  4. React18 + vite

主应用配置:

  1. 主应用需安装 qiankun 微前端架构 (本章节使用的是最新版本:2.10.16)

    pnpm add qiankun 或者 # yarn add qiankun # 或者 npm i qiankun -S
    
  2. 注册微应用:在 src 目录新增 qiankun/index.ts

    import {registerMicroApps, start, addGlobalUncaughtErrorHandler} from 'qiankun'
    import type {RegistrableApp, FrameworkLifeCycles} from 'qiankun/lib'
    
    // 微应用注册信息
    const apps: RegistrableApp<object>[] = [
      {
        name: 'sonVue2', // 微应用名称
        entry: '//localhost:3002/', // 微应用入口
        container: '#container', // 微应用挂载节点
        activeRule: '/son-vue2', // 微应用路由
      },
      {
        name: 'sonVue3',
        entry: '//localhost:3003/',
        container: '#container',
        activeRule: '/son-vue3',
      },
      {
        name: 'sonReact1',
        entry: '//localhost:3004/',
        container: '#container',
        activeRule: '/son-react1',
      },
      {
        name: 'sonReact2',
        entry: '//localhost:3005/',
        container: '#container', 
        activeRule: '/son-react2',
      }
    ]
    
    // 全局的微应用生命周期钩子
    const lifecycle: FrameworkLifeCycles<Object> = {
      beforeLoad: async (app) => {
        console.log(app)
      },
      beforeMount: async (app) => {
        console.log(app)
      },
      afterMount: async (app) => {
        console.log(app)
      },
      beforeUnmount: async (app) => {
        console.log(app)
      },
      afterUnmount: async (app) => {
        console.log(app)
      }
    }
    
    registerMicroApps(apps, lifecycle)
    
    // 添加全局的未捕获异常处理器
    addGlobalUncaughtErrorHandler((event: any) => {
      const {message: msg} = event
      if (msg && msg.includes('died in status LOADING_SOURCE_CODE')) {
        console.error('微应用加载失败')
      } else {
        console.error(event)
      }
    })
    export default start
    
  3. 封装 qiankun 全局通信方法(可选):在 src 目录新增 qiankun/actions.ts

    import {initGlobalState} from 'qiankun'
    import type {MicroAppStateActions, OnGlobalStateChangeCallback} from 'qiankun/lib'
    
    class Actions {
      declare actions: MicroAppStateActions
    
      setAction(state: Record<string, any>): void {
        this.actions = initGlobalState(state)
      }
    
      onGlobalStateChange(callback: OnGlobalStateChangeCallback, fireImmediately?: boolean): void {
        return this.actions.onGlobalStateChange(callback, fireImmediately)
      }
    
      setGlobalState(state: Record<string, any>): boolean {
        return this.actions.setGlobalState(state)
      }
    
      offGlobalStateChange(): boolean {
        return this.actions.offGlobalStateChange()
      }
    }
    
    const actions = new Actions()
    export default actions
    
  4. vue 路由配置

    import {createRouter, createWebHistory} from 'vue-router'
    
    const router = createRouter({
      routes: [...],
      // 路由模式配置为history模式
      history: createWebHistory(import.meta.env.BASE_URL)
    })
    // 处理微应用内部路由跳转后无法切换到主应用或其他微应用问题
    router.afterEach((to) => {
      const current = router.options.history.state.current
      Object.assign(router.options.history.state, {
        current: current === '/' ? '/' : to.fullPath
      })
    })
    
    export default router
    
  5. main.ts 中应用

    import '@/assets/main.scss'
    import {createApp} from 'vue'
    import App from './App.vue'
    import router from './router'
    import {createPinia} from "pinia";
    import {useMessageStore} from "@/stores/message";
    import start from '@/qiankun/index' // 引用注册微应用文件
    import actions from '@/qiankun/actions'; // 引用qiankun通信
    
    const app = createApp(App)
    app.use(router)
    app.use(createPinia())
    app.mount('#host-app')
    
    const store = useMessageStore()
    
    // 设置全局状态
    actions.setAction({message: store.message})
    // 监听全局状态
    actions.onGlobalStateChange((state, prev) => {
      store.setMessage(state.message)
    })
    
    // 启动qiankun
    start({
      prefetch: true, // 开启预加载
      sandbox: true // 开启沙箱
    })
    
  6. 修改 App.vue

    <script setup lang="ts">
        ...
    </script>
    
    <template>
    	...
    	<!-- 微应用挂载节点 -->
        <div id="container"></div>
    </template>
    
    <style scoped lang="scss">
        ...
    </style>
    

微应用搭建:

Vue2 + Webpack

微应用无需安装额外的依赖。

  1. src 目录新增 qiankun/public-path.js

    if (window.__POWERED_BY_QIANKUN__) {
        __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
    }
    
  2. 修改 vue.config.js 配置

    const {defineConfig} = require('@vue/cli-service')
    
    module.exports = defineConfig({
        devServer: {
        	port: 3002,
            host: '0.0.0.0',
            // 允许本地跨域请求
            headers: {
                'Access-Control-Allow-Origin': '*',
            },
        },
        // 配置公共访问路径
        publicPath: '/micro-app-vue2/',
        configureWebpack: {
           output: {
                library: `sonVue2`, // 微应用名称
                libraryTarget: 'umd', // 打包成 umd 库格式
                chunkLoadingGlobal: `webpackJsonp_sonVue2`,
           },
        },
    })
    
  3. 封装 qiankun 全局通信方法(可选):在 src 目录新增 qiankun/actions.js

    class Actions {
        actions = {
            onGlobalStateChange: null,
            setGlobalState: null,
        }
    
        initAction(actions) {
            this.actions = actions
        }
    
        onGlobalStateChange() {
            return this.actions.onGlobalStateChange(...arguments)
        }
    
        setGlobalState() {
            return this.actions.setGlobalState(...arguments)
        }
    }
    
    const actions = new Actions()
    export default actions
    
  4. vue 路由配置

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    
    Vue.use(VueRouter)
    
    const router = new VueRouter({
     	routes:[...],
        base: window.__POWERED_BY_QIANKUN__ ? '/son-vue2' : process.env.BASE_URL', // 配置微应用访问路径
        mode: 'history', // 路由模式配置为history模式
    })
    
    export default router
    
  5. main.js 中应用

    import '@/qiankun/public-path';
    import Vue from 'vue'
    import App from './App.vue'
    import router from './router'
    import actions from "@/qiankun/actions"; // 引用qiankun通信
    
    Vue.config.productionTip = false
    let app = null;
    
    function render(props = {}) {
        const {container} = props;
        app = new Vue({
            router,
            render: (h) => h(App),
        }).$mount(container ? container.querySelector('#app') : '#app');
    }
    
    // 独立运行时
    if (!window.__POWERED_BY_QIANKUN__) {
        render();
    }
    
    // 导出微应用生命周期钩子
    // 初始化
    export async function bootstrap() {}
    
    // 挂载
    export async function mount(props) {
        actions.initAction(props) // 初始化qiankun通信
        render(props);
    }
    
    // 更新
    export async function update(){}
    
    // 卸载
    export async function unmount() {
        app.$destroy();
        app.$el.innerHTML = '';
        app = null;
    }
    

Vue3 + Vite

由于是 Vite 应用需要安装 Vite 版本的 qiankun 依赖

pnpm add vite-plugin-qiankun -D 或者 # yarn add vite-plugin-qiankun -D # 或者 npm i vite-plugin-qiankun -D
  1. 修改 vite.config.js 配置

    import {fileURLToPath, URL} from 'node:url'
    
    import {defineConfig} from 'vite'
    import vue from '@vitejs/plugin-vue'
    import qiankun from 'vite-plugin-qiankun'
    
    export default defineConfig({
        plugins: [
            vue(),
         	// 配置qiankun    
            qiankun('sonVue3', {
                useDevMode: true
            })
        ],
    
        base: '/micro-app-vue3/',
        server: {
            port: 3003,
            host: '0.0.0.0',
            cors: true, // 允许本地跨域请求
        },
        
        resolve: {
            alias: {
                '@': fileURLToPath(new URL('./src', import.meta.url))
            }
        }
    })
    
  2. 封装 qiankun 全局通信方法(可选):在 src 目录新增 qiankun/actions.js

    class Actions {
        actions = {
            onGlobalStateChange: null,
            setGlobalState: null,
        }
    
        initAction(actions) {
            this.actions = actions
        }
    
        onGlobalStateChange() {
            return this.actions.onGlobalStateChange(...arguments)
        }
    
        setGlobalState() {
            return this.actions.setGlobalState(...arguments)
        }
    }
    
    const actions = new Actions()
    export default actions
    
  3. vue 路由配置

    import {createRouter, createWebHistory} from 'vue-router'
    import {qiankunWindow} from 'vite-plugin-qiankun/dist/helper.js'
    const router = createRouter({
        routes: [...],
        // 路由模式配置为history模式,并配置微应用访问路径
        history: createWebHistory(qiankunWindow.__POWERED_BY_QIANKUN__ ? '/son-vue3' : import.meta.env.BASE_URL),
    })
    
    export default router
    
  4. main.js 中应用

    import './assets/main.css'
    
    import {createApp} from 'vue'
    import App from './App.vue'
    import router from './router'
    import {renderWithQiankun, qiankunWindow} from 'vite-plugin-qiankun/dist/helper'
    import actions from "@/qiankun/actions.js"; // 引用qiankun通信
    
    let app = null
    
    function render(props = {}) {
        const {container} = props;
        app = createApp(App)
        app.use(router)
        app.mount(container ? container.querySelector('#app') : '#app');
    }
    
    // 如果不是在 qiankun 环境中运行,则独立启动
    if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
        render()
    }
    
    // 导出微应用生命周期钩子
    renderWithQiankun({
        // 初始化
        function bootstrap() {}
        // 挂载
        function mount(props) {
            actions.initAction(props) // 初始化qiankun通信
            render(props)
        }
        // 更新
        update: () => {},
        // 卸载
        function unmount(props) {
            app.unmount();
        }
    })
    

React18 + Webpack

微应用无需安装额外的依赖。

  1. src 目录新增 qiankun/public-path.js

    if (window.__POWERED_BY_QIANKUN__) {
        __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
    }
    
  2. 需要修改webpack需要安装@craco/craco 依赖

    pnpm add @craco/craco -D 或者 # yarn add @craco/craco -D # 或者 npm i @craco/craco -D
    

    修改 craco.config.js 配置

    const path = require('path')
    module.exports = {
      devServer: {
        port: 3004,
        hot: true,
        host: '0.0.0.0',
        headers: {
          'Access-Control-Allow-Origin': '*'
        },
      },
      webpack: {
        configure: (webpackConfig, { paths }) => {
          webpackConfig.output = {
            ...webpackConfig.output,
            path: path.resolve(__dirname, 'micro-app-react1'),
            library: 'sonReact1',
            libraryTarget: 'umd',
            chunkLoadingGlobal: 'webpackJsonp_sonReact1',
            globalObject: 'window',
          }
          // 返回修改后的配置
          return webpackConfig
        },
        alias:{
          	"@": path.resolve(__dirname, './src')
        }
      },
    }
    
  3. 封装 qiankun 全局通信方法(可选):在 src 目录新增 qiankun/actions.js

    class Actions {
      actions = {}
      initAction (actions) {
        this.actions = actions
      }
    
      onGlobalStateChange () {
        return this.actions.onGlobalStateChange(...arguments)
      }
    
      setGlobalState () {
        return this.actions.setGlobalState(...arguments)
      }
    }
    
    const actions = new Actions()
    export default actions
    
  4. react 路由配置

    import { createBrowserRouter } from 'react-router-dom'
    import { lazy, Suspense } from 'react'
    import isQiankun from '@/qiankun/isQiankun'
    
    const router = createBrowserRouter(
      [...], 
      {
      	basename: window.__POWERED_BY_QIANKUN__ ? '/son-react1' : '/micro-app-react1'
      }
    )
    export default router
    
  5. index.js 中应用

    import { createRoot } from 'react-dom/client'
    import './index.scss'
    import "@/qiankun/public-path"
    import { RouterProvider } from 'react-router-dom'
    import router from '@/router'
    import actions from '@/qiankun/actions'
    
    let root = null
    
    function render (props = {}) {
      	const { container } = props
      	root = createRoot(container ? container.querySelector('#app') : document.querySelector('#app'))
      	root.render(<RouterProvider router={router}/>)
    }
    
    // 独立运行时
    if (!window.__POWERED_BY_QIANKUN__) {
      render({})
    }
    
    // 导出微应用生命周期钩子
    // 初始化
    export async function bootstrap () {}
    // 挂载
    export async function mount (props) {
    	actions.initAction(props) // 初始化qiankun通信
      	render(props)
    }
    // 更新
    export async function update () {}
    // 卸载
    export async function unmount () {
      	root.unmount()
      	root = null
    }
    

React18 + Vite

由于是 Vite 应用需要安装 Vite 版本的 qiankun 依赖

pnpm add vite-plugin-qiankun -D 或者 # yarn add vite-plugin-qiankun -D # 或者 npm i vite-plugin-qiankun -D
  1. 修改 vite.config.ts 配置

    import {fileURLToPath, URL} from 'node:url'
    import {defineConfig} from 'vite'
    import react from '@vitejs/plugin-react'
    import qiankun from 'vite-plugin-qiankun'
    
    export default defineConfig({
      plugins: [
        react(),
        qiankun('sonReact2', {
          useDevMode: true
        })
      ],
      base: '/micro-app-react2/',
      server: {
        port: 3005,
        host: '0.0.0.0',
        cors: true,
        hmr: false, // 在本地开发测试时,需要关闭热更新
      },
      resolve: {
        alias: {
          '@': fileURLToPath(new URL('./src', import.meta.url))
        }
      }
    })
    
  2. 封装 qiankun 全局通信方法(可选):在 src 目录新增 qiankun/actions.ts

    import {QiankunProps} from "vite-plugin-qiankun/es/helper";
    
    type OnGlobalStateChangeCallback = (state: Record<string, any>, prevState: Record<string, any>) => void
    
    class Actions {
      actions: QiankunProps | null = null
    
      initAction(actions: QiankunProps) {
        this.actions = actions
      }
    
      onGlobalStateChange(callback: OnGlobalStateChangeCallback, fireImmediately?: boolean) {
        return this.actions?.onGlobalStateChange(callback, fireImmediately)
      }
    
      setGlobalState(state: Record<string, any>): boolean {
        return this.actions?.setGlobalState(state)
      }
    }
    
    const actions = new Actions()
    export default actions
    
  3. react 路由配置

    import {createBrowserRouter} from "react-router-dom";
    import {qiankunWindow} from 'vite-plugin-qiankun/dist/helper'
    
    const router = createBrowserRouter(
      [...],
      {
        basename: qiankunWindow.__POWERED_BY_QIANKUN__ ? "/son-react2" : import.meta.env.BASE_URL
      }
    )
    
    export default router
    
  4. main.ts 中应用

    import {createRoot} from 'react-dom/client'
    import './index.scss'
    import {RouterProvider} from "react-router-dom";
    import router from "@/router";
    import {qiankunWindow} from 'vite-plugin-qiankun/dist/helper'
    import {renderWithQiankun} from "vite-plugin-qiankun/dist/helper";
    import actions from "@/qiankun/actions.ts"; // 引用qiankun通信
    
    type propsType = {
      container?: ReactDOM.Container,
    }
    let root: ReturnType<typeof createRoot> | null = null
    
    function render(props: propsType = {}) {
      const {container} = props;
      root = createRoot(container ? container.querySelector("#app")! : document.querySelector("#app")!)
      root.render(
        <RouterProvider router={router}/>
      )
    }
    
    // 如果不是在 qiankun 环境中运行,则独立启动
    if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
      render({})
    }
    
    // 导出微应用生命周期钩子
    renderWithQiankun({
      // 初始化
      bootstrap: () => {},
      // 挂载
      mount: (props) => {
        // 初始化qiankun通信
        actions.initAction(props)
        render(props)
      },
      // 更新
      update: () => {},
      // 卸载
      unmount: () => {
        root && root.unmount()
        root = null
      },
    })
    
    
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值