侧边栏壁纸
博主头像
xuesheng博主等级

分享web知识,学习就是取悦自己!

  • 累计撰写 118 篇文章
  • 累计创建 14 个标签
  • 累计收到 3 条评论

目 录CONTENT

文章目录

vite/vue3配置MicroApp

xuesheng
2022-09-08 / 0 评论 / 0 点赞 / 659 阅读 / 1,476 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-09-08,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

vite/vue3配置MicroApp

安装依赖

npm i @micro-zoe/micro-app --save

路由router.js:

{

        // 因为主应用为history路由,appname-vite子应用是hash路由,这里配置略微不同

        // 已解决带参数时页面丢失的问题

        path: '/app-vite:page*',

        name: 'vite',

        component: () => import('@/views/vite.vue')

}

vite路由页面:

<template>
    <div>
        <micro-app
            name="appname-vite"
            :url="url"
            inline
            disablesandbox
            :data="microAppData"
            @created="handleCreate"
            @beforemount="handleBeforeMount"
            @mounted="handleMount"
            @unmount="handleUnmount"
            @error="handleError"
            @datachange="handleDataChange"
        ></micro-app>
    </div>
</template>

<script lang="ts">
import { EventCenterForMicroApp } from '@micro-zoe/micro-app';
import config from '../config';

// @ts-ignore 因为vite子应用关闭了沙箱,我们需要为子应用appname-vite创建EventCenterForMicroApp对象来实现数据通信
window.eventCenterForAppNameVite = new EventCenterForMicroApp('appname-vite');

export default {
    name: 'vite',
    data () {
        return {
            url: `${config.vite}/child/vite/`,
            microAppData: { msg: '来自基座的数据' }
        };
    },
    methods: {
        handleCreate (): void {
            console.log('child-vite 创建了');
        },

        handleBeforeMount (): void {
            console.log('child-vite 即将被渲染');
        },

        handleMount (): void {
            console.log('child-vite 已经渲染完成');

            setTimeout(() => {
                // @ts-ignore
                this.microAppData = { msg: '来自基座的新数据' };
            }, 2000);
        },

        handleUnmount (): void {
            console.log('child-vite 卸载了');
        },

        handleError (): void {
            console.log('child-vite 加载出错了');
        },

        handleDataChange (e: CustomEvent): void {
            console.log('来自子应用 child-vite 的数据:', e.detail.data);
        }
    }
};
</script>

main.ts中:

import microApp from '@micro-zoe/micro-app';
 microApp.start({
        plugins: {
            modules: {
             //咱们的子应用是vite项目,在这配置
                'appname-vite': [
                    {
                        loader(code: string) {
                            if (process.env.NODE_ENV === 'development') {
                                // 这里 /basename/ 需要和子应用vite.config.js中base的配置保持一致
                                code = code.replace(/(from|import)(\s*['"])(\/child\/vite\/)/g, all => {
                                    return all.replace('/child/vite/', 'http://localhost:4007/child/vite/');
                                });
                            }

                            return code;
                        }
                    }
                ]
            }
        }
    });

vite.config中:

import { defineConfig,searchForWorkspaceRoot } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path'; // 编辑器提示 path 模块找不到,可以yarn add @types/node --dev
import stylelintPlugin from 'vite-plugin-stylelint';

export default defineConfig({
    plugins: [vue({
        template: {
            compilerOptions: {
                isCustomElement: tag => /^micro-app/.test(tag)
            }
        }
    }),
        stylelintPlugin({
            include: ['src/**/*.css', 'src/**/*.less', 'src/**/*.scss', 'src/**/*.sass', 'src/**/*.vue'],
            cache: false
        })
    ],
    resolve: {
        alias: {
            '@': resolve(__dirname, 'src') // 设置 `@` 指向 `src` 目录
        }
    },
    server: {
        port: 3000, // 设置服务启动端口号
        open: true ,// 设置服务启动时是否自动打开浏览器
        fs: {
            allow: [
                searchForWorkspaceRoot(process.cwd()),
                '/'
            ]
        }
    },
    base: '/main/',
    build: {
        outDir: 'main'
    }
});

以上就是主应用的改造,如果不需要路由,子应用只作为一个组件的话,和引入一个vue组件的方式相似。

子应用的main.ts中

import { createApp, App as AppInstance } from 'vue'
import { createRouter, createWebHashHistory, RouterHistory, Router } from 'vue-router'
import App from './App.vue'
import routes from './router'

declare global {
  interface Window {
    eventCenterForAppNameVite: any
    __MICRO_APP_NAME__: string
    __MICRO_APP_ENVIRONMENT__: string
    __MICRO_APP_BASE_APPLICATION__: string
  }
}

// 与基座进行数据交互
function handleMicroData (router: Router) {
  // eventCenterForAppNameVite 是基座添加到window的数据通信对象
  if (window.eventCenterForAppNameVite) {
    // 主动获取基座下发的数据
    console.log('child-vite getData:', window.eventCenterForAppNameVite.getData())

    // 监听基座下发的数据变化
    window.eventCenterForAppNameVite.addDataListener((data: Record<string, unknown>) => {
      console.log('child-vite addDataListener:', data)

      if (data.path && typeof data.path === 'string') {
        data.path = data.path.replace(/^#/, '')
        // 当基座下发path时进行跳转
        if (data.path && data.path !== router.currentRoute.value.path) {
          router.push(data.path as string)
        }
      }
    })

    // 向基座发送数据
    setTimeout(() => {
      window.eventCenterForAppNameVite.dispatch({ myname: 'child-vite' })
    }, 3000)
  }
}

/**
 * 用于解决主应用和子应用都是vue-router4时相互冲突,导致点击浏览器返回按钮,路由错误的问题。
 * 相关issue:https://github.com/micro-zoe/micro-app/issues/155
 * 当前vue-router版本:4.0.12
 */
 function fixBugForVueRouter4 (router: Router) {
  // 判断主应用是main-vue3或main-vite,因为这这两个主应用是 vue-router4
  if (window.location.href.includes('/main-vue3') || window.location.href.includes('/main-vite')) {
    /**
     * 重要说明:
     * 1、这里主应用下发的基础路由为:`/main-xxx/app-vite`,其中 `/main-xxx` 是主应用的基础路由,需要去掉,我们只取`/app-vite`,不同项目根据实际情况调整
     *
     * 2、因为vite关闭了沙箱,又是hash路由,我们这里写死realBaseRoute为:/app-vite#
     */
     const realBaseRoute = '/app-vite#'

     router.beforeEach(() => {
       if (typeof window.history.state?.current === 'string') {
         window.history.state.current = window.history.state.current.replace(new RegExp(realBaseRoute, 'g'), '')
       }
     })

     router.afterEach(() => {
       if (typeof window.history.state === 'object') {
         window.history.state.current = realBaseRoute +  (window.history.state.current || '')
       }
     })
  }
}


// ----------分割线---umd模式------两种模式任选其一-------------- //
let app: AppInstance | null = null
let router: Router | null = null
let history: RouterHistory | null = null
// 将渲染操作放入 mount 函数
function mount () {
  history = createWebHashHistory()
  router = createRouter({
    history,
    routes,
  })

  app = createApp(App)
  app.use(router)
  app.mount('#vite-app')

  console.log('微应用child-vite渲染了')

  handleMicroData(router)

  // fixBugForVueRouter4(router)
}

// 将卸载操作放入 unmount 函数
function unmount () {
  app?.unmount()
  history?.destroy()
  // 卸载所有数据监听函数
  window.eventCenterForAppNameVite?.clearDataListener()
  app = null
  router = null
  history = null
  console.log('微应用child-vite卸载了')
}

// 微前端环境下,注册mount和unmount方法
if (window.__MICRO_APP_BASE_APPLICATION__) {
  // @ts-ignore
  window['micro-app-appname-vite'] = { mount, unmount }
} else {
  // 非微前端环境直接渲染
  mount()
}

vite.config.ts

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { join } from 'path'
import { writeFileSync } from 'fs'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    (function () {
      let basePath = ''
      return {
        name: "vite:micro-app",
        apply: 'build',
        configResolved(config) {
          basePath = `${config.base}${config.build.assetsDir}/`
        },
        // writeBundle 钩子可以拿到完整处理后的文件,但已经无法修改
        writeBundle (options, bundle) {
          for (const chunkName in bundle) {
            if (Object.prototype.hasOwnProperty.call(bundle, chunkName)) {
              const chunk = bundle[chunkName]
              if (chunk.fileName && chunk.fileName.endsWith('.js')) {
                chunk.code = chunk.code.replace(/(from|import\()(\s*['"])(\.\.?\/)/g, (all, $1, $2, $3) => {
                  return all.replace($3, new URL($3, basePath))
                })
                const fullPath = join(options.dir, chunk.fileName)
                writeFileSync(fullPath, chunk.code)
              }
            }
          }
        },
      }
    })() as any,
  ],
  server: {
    port: 4007,
  },
  base: `${process.env.NODE_ENV === 'production' ? 'http://www.micro-zoe.com' : ''}/child/vite/`,
  build: {
    outDir: 'vite',
  },
})

附录:MicroApp中文文档 http://cangdu.org/micro-app/docs.html#/zh-cn/framework/vue

0

评论区