Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vmicro #4938

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open

Vmicro #4938

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions apps/web-antd/.env
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@ VITE_APP_TITLE=Vben Admin Antd

# 应用命名空间,用于缓存、store等功能的前缀,确保隔离
VITE_APP_NAMESPACE=vben-web-antd

# public path
VITE_BASE=/vben-antd/

# 是否注入全局loading
VITE_INJECT_APP_LOADING=false
3 changes: 1 addition & 2 deletions apps/web-antd/.env.analyze
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# public path
VITE_BASE=/


# Basic interface address SPA
VITE_GLOB_API_URL=/api
Expand Down
5 changes: 1 addition & 4 deletions apps/web-antd/.env.development
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# 端口号
VITE_PORT=5666

VITE_BASE=/

# 接口地址
VITE_GLOB_API_URL=/api

Expand All @@ -12,5 +10,4 @@ VITE_NITRO_MOCK=true
# 是否打开 devtools,true 为打开,false 为关闭
VITE_DEVTOOLS=false

# 是否注入全局loading
VITE_INJECT_APP_LOADING=true

4 changes: 0 additions & 4 deletions apps/web-antd/.env.production
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
VITE_BASE=/

# 接口地址
VITE_GLOB_API_URL=https://mock-napi.vben.pro/api
Expand All @@ -12,8 +11,5 @@ VITE_PWA=false
# vue-router 的模式
VITE_ROUTER_HISTORY=hash

# 是否注入全局loading
VITE_INJECT_APP_LOADING=true

# 打包后是否生成dist.zip
VITE_ARCHIVER=true
6 changes: 3 additions & 3 deletions apps/web-antd/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<!-- 由 vite 注入 VITE_APP_TITLE 变量,在 .env 文件内配置 -->
<title><%= VITE_APP_TITLE %></title>
<link rel="icon" href="/favicon.ico" />
<script>
<!-- <script>
// 生产环境下注入百度统计
if (window._VBEN_ADMIN_PRO_APP_CONF_) {
var _hmt = _hmt || [];
Expand All @@ -26,10 +26,10 @@
s.parentNode.insertBefore(hm, s);
})();
}
</script>
</script> -->
</head>
<body>
<div id="app"></div>
<div id="antd-app" class="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
1 change: 1 addition & 0 deletions apps/web-antd/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"#/*": "./src/*"
},
"dependencies": {
"@micro-zoe/micro-app": "catalog:",
"@vben/access": "workspace:*",
"@vben/common-ui": "workspace:*",
"@vben/constants": "workspace:*",
Expand Down
55 changes: 51 additions & 4 deletions apps/web-antd/src/bootstrap.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createApp, watchEffect } from 'vue';
import { type App as AppInstance, createApp, watchEffect } from 'vue';

import { registerAccessDirective } from '@vben/access';
import { preferences } from '@vben/preferences';
Expand All @@ -12,13 +12,28 @@ import { $t, setupI18n } from '#/locales';

import { initComponentAdapter } from './adapter/component';
import App from './app.vue';
// import { type Router } from 'vue-router';
import { router } from './router';
import { handleDataListener } from './useChildMicro';

async function bootstrap(namespace: string) {
/**
* 与基座进行数据交互
*/
function handleMicroData() {
// 是否是微前端环境
if (window.__MICRO_APP_ENVIRONMENT__) {
// 主动获取基座下发的数据
window.microApp.addDataListener(handleDataListener, true);
}
}

let app: AppInstance | null = null;
// 将渲染操作放入 mount 函数
async function mount(namespace: string) {
// 初始化组件适配器
await initComponentAdapter();

const app = createApp(App);
app = createApp(App);

// 国际化 i18n 配置
await setupI18n(app);
Expand All @@ -42,7 +57,39 @@ async function bootstrap(namespace: string) {
}
});

app.mount('#app');
app.mount('#antd-app');

handleMicroData();
}
// 将卸载操作放入 unmount 函数
function unmount() {
app?.unmount();
// history?.destroy();
// 卸载所有数据监听函数,开启沙箱情况下不再需要
window.microApp.clearDataListener();

app = null;
// router = null;
// history = null;
console.warn('微应用卸载了,微应用的 unmount() fired');
}

Comment on lines +65 to +75
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add environment check before clearing data listeners

To prevent potential runtime errors when window.microApp is undefined outside of the micro-frontend environment, consider adding a check for window.__MICRO_APP_ENVIRONMENT__ before calling window.microApp.clearDataListener().

Apply the following change to ensure safe execution:

function unmount() {
  app?.unmount();
  // history?.destroy();
  // 卸载所有数据监听函数,开启沙箱情况下不再需要
+  if (window.__MICRO_APP_ENVIRONMENT__) {
     window.microApp.clearDataListener();
+  }
  app = null;
  // router = null;
  // history = null;
  console.warn('微应用卸载了,微应用的 unmount() fired');
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function unmount() {
app?.unmount();
// history?.destroy();
// 卸载所有数据监听函数,开启沙箱情况下不再需要
window.microApp.clearDataListener();
app = null;
// router = null;
// history = null;
console.warn('微应用卸载了,微应用的 unmount() fired');
}
function unmount() {
app?.unmount();
// history?.destroy();
// 卸载所有数据监听函数,开启沙箱情况下不再需要
if (window.__MICRO_APP_ENVIRONMENT__) {
window.microApp.clearDataListener();
}
app = null;
// router = null;
// history = null;
console.warn('微应用卸载了,微应用的 unmount() fired');
}

async function bootstrap(namespace: string) {
if (window.__MICRO_APP_ENVIRONMENT__) {
console.warn('检测微应用环境', window.__MICRO_APP_ENVIRONMENT__);
const EXPOSE_MICRO_APP_NAME = `vben-micro-${window.__MICRO_APP_NAME__}`;
// 微前端环境下挂载到 window 上
// @ts-ignore 为了规避 ts 类型检查
(window as any)[EXPOSE_MICRO_APP_NAME] = {
mount: async (ns: string) => await mount(ns),
unmount,
};
await (window as any)[EXPOSE_MICRO_APP_NAME].mount(namespace);
} else {
console.warn('非微前端环境');
Gocas marked this conversation as resolved.
Show resolved Hide resolved
Gocas marked this conversation as resolved.
Show resolved Hide resolved
// 非微前端环境直接渲染
mount(namespace);
}
}

export { bootstrap };
5 changes: 4 additions & 1 deletion apps/web-antd/src/layouts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@ const BasicLayout = () => import('./basic.vue');
const AuthPageLayout = () => import('./auth.vue');

const IFrameView = () => import('@vben/layouts').then((m) => m.IFrameView);
const LayoutUI = () => import('@vben/layouts').then((m) => m.LayoutUI);
const LayoutContent = () =>
import('@vben/layouts').then((m) => m.LayoutContent);

export { AuthPageLayout, BasicLayout, IFrameView };
export { AuthPageLayout, BasicLayout, IFrameView, LayoutContent, LayoutUI };
1 change: 1 addition & 0 deletions apps/web-antd/src/preferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ export const overridesPreferences = defineOverridesPreferences({
// overrides
app: {
name: import.meta.env.VITE_APP_TITLE,
// layout: 'full-content',
},
});
3 changes: 2 additions & 1 deletion apps/web-antd/src/router/access.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { preferences } from '@vben/preferences';
import { message } from 'ant-design-vue';

import { getAllMenusApi } from '#/api';
import { BasicLayout, IFrameView } from '#/layouts';
import { BasicLayout, IFrameView, LayoutContent } from '#/layouts';
import { $t } from '#/locales';

const forbiddenComponent = () => import('#/views/_core/fallback/forbidden.vue');
Expand All @@ -19,6 +19,7 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) {

const layoutMap: ComponentRecordType = {
BasicLayout,
LayoutContent,
IFrameView,
};

Expand Down
2 changes: 1 addition & 1 deletion apps/web-antd/src/router/routes/modules/dashboard.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { RouteRecordRaw } from 'vue-router';

// LayoutContent
import { BasicLayout } from '#/layouts';
import { $t } from '#/locales';

Expand Down
5 changes: 3 additions & 2 deletions apps/web-antd/src/router/routes/modules/demos.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import type { RouteRecordRaw } from 'vue-router';

import { BasicLayout } from '#/layouts';
// BasicLayout
import { LayoutContent } from '#/layouts';
import { $t } from '#/locales';

const routes: RouteRecordRaw[] = [
{
component: BasicLayout,
component: LayoutContent,
Gocas marked this conversation as resolved.
Show resolved Hide resolved
meta: {
icon: 'ic:baseline-view-in-ar',
keepAlive: true,
Expand Down
5 changes: 3 additions & 2 deletions apps/web-antd/src/router/routes/modules/vben.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ import {
VBEN_NAIVE_PREVIEW_URL,
} from '@vben/constants';

import { BasicLayout, IFrameView } from '#/layouts';
// BasicLayout
import { IFrameView, LayoutContent } from '#/layouts';
import { $t } from '#/locales';

const routes: RouteRecordRaw[] = [
{
component: BasicLayout,
component: LayoutContent,
meta: {
badgeType: 'dot',
icon: VBEN_LOGO_URL,
Expand Down
90 changes: 90 additions & 0 deletions apps/web-antd/src/useChildMicro.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// 子服务设置

// useUserStore
import { useAccessStore } from '@vben/stores';
// preferences
import { updatePreferences } from '@vben/preferences';

import { type EventCenterForMicroApp } from '@micro-zoe/micro-app';

import { router } from './router';

declare global {
interface Window {
// 关闭沙箱时,不存在window.eventCenterForAppNameVite对象
// eventCenterForAppNameVite: any;
microApp: {
dispatch(data: { AppName: string; data: unknown }): void;
getData(): Record<string, unknown>;
} & EventCenterForMicroApp;
__MICRO_APP_NAME__: string;
__MICRO_APP_ENVIRONMENT__: string;
__MICRO_APP_BASE_APPLICATION__: string;
}
}
// const MAIN_ROUTER = window.microApp.router.getBaseAppRouter();

/**
* 与基座进行数据交互
* 在不关闭沙箱的模式下,子应用中的window为代理对象,和真正的window对象(windows.rawWindow)不是一个对象.
* 重要属性:
* window.rawWindow 原始window对象-基座的window对象
* window.microApp<EventCenterForMicroApp>.getData() 获得基座传入的参数
* windows.__MICRO_APP_BASE_APPLICATION__
* windows.rawWindow.eventCenterForAppNameVite==undefined
* @param {IMicroAppData} data
*/
interface IMicroAppData {
accessToken?: string /* token */;
hash?: string /* 路由地址 */;
mainPreferences?: Record<string, unknown> /* 系统配置项 */;
}
function handleDataListener(data: IMicroAppData & Record<string, any>) {
// 以下代码为开启沙箱时的代码
try {
if (!data || typeof data !== 'object') {
throw new Error('Invalid data received from base application');
}
const {
accessToken = undefined,
hash = '/',
mainPreferences = undefined,
} = data;

// Object.keys(data).forEach((key) => {
// console.warn(`子应用获得基座数据 ${key}: `, data[key]);
// });

const accessStore = useAccessStore();
if (accessToken) accessStore.setAccessToken(accessToken);
if (mainPreferences) updatePreferences(mainPreferences);

// 当基座下发path时进行跳转
if (hash === router.currentRoute.value.hash) {
console.warn('基座下发的path为空或者与当前路由相同');
} else {
router.push(hash as string);
console.warn('router.currentRoute:::', router.currentRoute);
// history.replaceState(history.state, '', hash);
}
// // 向基座发送数据
// setTimeout(() => {
// // MAIN_ROUTER.push('/');
// window.microApp.dispatch({
// AppName: data.AppName,
// data: '向基座发送数据' + data.hash,
// });
// }, 3000);
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : 'Unknown error';
console.error(`[${data.AppName}] 数据处理失败:`, errorMessage);
window.microApp?.dispatch({
type: 'error',
AppName: data.AppName,
data: { message: errorMessage },
});
}
}
Gocas marked this conversation as resolved.
Show resolved Hide resolved

export { handleDataListener };
1 change: 1 addition & 0 deletions apps/web-antd/vite.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export default defineConfig(async () => {
application: {},
vite: {
server: {
open: true,
proxy: {
'/api': {
changeOrigin: true,
Expand Down
5 changes: 5 additions & 0 deletions internal/vite-config/src/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ async function loadCommonPlugins(
defineModel: true,
// propsDestructure: true,
},
template: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('micro-app'),
},
},
}),
viteVueJsx(),
],
Expand Down
4 changes: 2 additions & 2 deletions internal/vite-config/src/plugins/nitro-mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const viteNitroMockPlugin = ({
_printUrls();

consola.log(
` ${colors.green('➜')} ${colors.bold('Nitro Mock Server')}: ${colors.cyan(`http://localhost:${port}/api`)}`,
` ${colors.green('➜')} ${colors.bold('Nitro Mock Server')}: ${colors.cyan(`http://127.0.0.1:${port}/api`)}`,
);
};
},
Expand Down Expand Up @@ -85,7 +85,7 @@ async function runNitroServer(rootDir: string, port: number, verbose: boolean) {
nitro.hooks.hookOnce('restart', reload);

const server = createDevServer(nitro);
await server.listen(port, { showURL: false });
await server.listen(port, { hostname: '0.0.0.0', showURL: false });
await prepare(nitro);
await build(nitro);

Expand Down
1 change: 1 addition & 0 deletions packages/@core/base/design/src/css/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
-moz-osx-font-smoothing: grayscale; */
}

.app,
#app,
body,
html {
Expand Down
9 changes: 9 additions & 0 deletions packages/@core/base/typings/src/vue-router.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@ interface RouteMeta {
* 菜单可以看到,但是访问会被重定向到403
*/
menuVisibleWithForbidden?: boolean;
/**
* 微服务配置
*/
micro?: {
baseroute: string; // 微服务基础路由
hash: string; // 微服务 hash
host: string; // 微服务地址
name: string; // 微服务名称
};
/**
* 在新窗口打开
*/
Expand Down
2 changes: 2 additions & 0 deletions packages/@core/composables/src/use-layout-style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export function useLayoutContentStyle() {
return {
height: `${height}px`,
left: `${left}px`,
// height: `calc(100vh - ${top}px)`, //`${height}px`,
minHeight: `calc(100vh - ${top}px)`,
position: 'fixed',
top: `${top}px`,
width: `${width}px`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ function onTransitionEnd() {
</div>
</template>

<style scoped>
<style lang="less" scoped>
.loader {
&::before {
animation: loader-shadow-ani 0.5s linear infinite;
Expand Down
Loading
Loading