配置好了vue2的子应用接入
This commit is contained in:
101
src/App.vue
Normal file
101
src/App.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<div id="main-app">
|
||||
<header class="app-header">
|
||||
<div class="logo" @click="$router.push('/')">
|
||||
<h1>MicroApp 主应用</h1>
|
||||
</div>
|
||||
<nav class="nav-links">
|
||||
<router-link to="/home">首页</router-link>
|
||||
<router-link to="/child-app">Vue2 子应用</router-link>
|
||||
</nav>
|
||||
</header>
|
||||
<main class="app-main">
|
||||
<router-view />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 根组件 — 提供全局布局(头部导航 + 内容区)
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 全局样式重置 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||
'Helvetica Neue', Arial, 'Microsoft YaHei', sans-serif;
|
||||
color: #333;
|
||||
background: #f5f6f7;
|
||||
}
|
||||
|
||||
#app {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#main-app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 头部导航 */
|
||||
.app-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 56px;
|
||||
padding: 0 24px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: #fff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.logo {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.logo h1 {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-links a {
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
text-decoration: none;
|
||||
padding: 6px 16px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.nav-links a:hover {
|
||||
color: #fff;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
.nav-links a.router-link-active {
|
||||
color: #fff;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 主内容区 */
|
||||
.app-main {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
51
src/config/subApps.ts
Normal file
51
src/config/subApps.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* 子应用配置列表
|
||||
*
|
||||
* 后续对接 Vue 2 子应用时,在此处添加/修改配置即可
|
||||
*
|
||||
* @see https://jd-opensource.github.io/micro-app/docs.html#/configure
|
||||
*/
|
||||
export interface SubAppConfig {
|
||||
/** 应用名称,全局唯一,字母开头 */
|
||||
name: string
|
||||
/** 子应用地址(开发环境填写 devServer 地址) */
|
||||
url: string
|
||||
/** 基座分配给子应用的路由前缀 */
|
||||
baseroute: string
|
||||
/** 是否使用 iframe 沙箱(Vite 子应用必须开启;Webpack 子应用可选) */
|
||||
iframe?: boolean
|
||||
/** 是否保活子应用,避免重复加载 */
|
||||
keepAlive?: boolean
|
||||
/** 路由模式:native | native-scope */
|
||||
routerMode?: 'native' | 'native-scope'
|
||||
/** 是否禁用样式隔离 */
|
||||
disableScopecss?: boolean
|
||||
/** 是否禁用沙箱 */
|
||||
disableSandbox?: boolean
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 当前对接的子应用列表
|
||||
// 后续对接 Vue 2 项目时,修改 url 为实际的子应用地址
|
||||
// ============================================================
|
||||
|
||||
export const subApps: SubAppConfig[] = [
|
||||
{
|
||||
name: 'vue2-app',
|
||||
// TODO: 替换为你的 Vue 2 子应用实际地址
|
||||
url: 'http://localhost:5173/',
|
||||
baseroute: '/child-app',
|
||||
// Vite 子应用必须开启 iframe 模式
|
||||
// (with 沙箱的 new Function() 不支持 ES Module 的 import/export 语法)
|
||||
iframe: true,
|
||||
keepAlive: true,
|
||||
routerMode: 'native'
|
||||
}
|
||||
]
|
||||
|
||||
/**
|
||||
* 根据名称查找子应用配置
|
||||
*/
|
||||
export function getSubAppConfig(name: string): SubAppConfig | undefined {
|
||||
return subApps.find((app) => app.name === name)
|
||||
}
|
||||
36
src/main.ts
Normal file
36
src/main.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { createApp } from 'vue'
|
||||
import microApp from '@micro-zoe/micro-app'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
// 启动 micro-app 微前端框架
|
||||
// https://jd-opensource.github.io/micro-app/docs.html#/start
|
||||
microApp.start({
|
||||
// 预加载:当浏览器空闲时预加载子应用,加快首屏速度
|
||||
preFetchApps: [],
|
||||
// 当子应用未匹配到路由时的默认行为
|
||||
// 'default-page' 或自定义地址
|
||||
// defaultPage: '',
|
||||
// 全局生命周期
|
||||
lifeCycles: {
|
||||
created(_e, appName) {
|
||||
console.log(`[micro-app] 子应用 ${appName} 被创建`)
|
||||
},
|
||||
beforemount(_e, appName) {
|
||||
console.log(`[micro-app] 子应用 ${appName} 即将挂载`)
|
||||
},
|
||||
mounted(_e, appName) {
|
||||
console.log(`[micro-app] 子应用 ${appName} 挂载完成`)
|
||||
},
|
||||
unmount(_e, appName) {
|
||||
console.log(`[micro-app] 子应用 ${appName} 已卸载`)
|
||||
},
|
||||
error(_e, appName) {
|
||||
console.error(`[micro-app] 子应用 ${appName} 加载错误:`, _e)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const app = createApp(App)
|
||||
app.use(router)
|
||||
app.mount('#app')
|
||||
29
src/router/index.ts
Normal file
29
src/router/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/home'
|
||||
},
|
||||
{
|
||||
path: '/home',
|
||||
name: 'home',
|
||||
component: () => import('@/views/Home.vue')
|
||||
},
|
||||
{
|
||||
// 子应用路由 — `:page*` 通配符匹配子应用内部所有路由
|
||||
// 例如:/child-app、/child-app/page1、/child-app/page2/xxx
|
||||
path: '/child-app/:page*',
|
||||
name: 'childApp',
|
||||
component: () => import('@/views/ChildApp.vue')
|
||||
}
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
// history 模式 — 主应用和子应用都使用 history 模式
|
||||
// 通过 baseroute 区分路由归属
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes
|
||||
})
|
||||
|
||||
export default router
|
||||
140
src/views/ChildApp.vue
Normal file
140
src/views/ChildApp.vue
Normal file
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<div class="child-app-wrapper">
|
||||
<!--
|
||||
micro-app 子应用容器
|
||||
使用 key 绑定 url,当 url 变化时重新加载子应用
|
||||
-->
|
||||
<micro-app
|
||||
:key="currentApp.url"
|
||||
:name="currentApp.name"
|
||||
:url="currentApp.url"
|
||||
:baseroute="currentApp.baseroute"
|
||||
:iframe="currentApp.iframe ?? false"
|
||||
:keep-alive="currentApp.keepAlive ?? true"
|
||||
:router-mode="currentApp.routerMode ?? 'native'"
|
||||
:disable-scopecss="currentApp.disableScopecss ?? false"
|
||||
:disable-sandbox="currentApp.disableSandbox ?? false"
|
||||
@created="onCreated"
|
||||
@beforemount="onBeforeMount"
|
||||
@mounted="onMounted"
|
||||
@unmount="onUnmount"
|
||||
@error="onError"
|
||||
>
|
||||
<!-- 子应用加载中的占位内容 -->
|
||||
<div class="loading-placeholder">
|
||||
<div class="spinner"></div>
|
||||
<p>正在加载子应用 {{ currentApp.name }}...</p>
|
||||
<p class="loading-hint">
|
||||
请确保子应用已启动:<code>{{ currentApp.url }}</code>
|
||||
</p>
|
||||
</div>
|
||||
</micro-app>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { subApps } from '@/config/subApps'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
/**
|
||||
* 当前正在使用的子应用配置
|
||||
*
|
||||
* 这里根据路由前缀匹配对应的子应用。
|
||||
* 如果只有一个子应用,直接使用第一个配置;
|
||||
* 多子应用时,需要根据 route.path 匹配 baseroute。
|
||||
*/
|
||||
const currentApp = computed(() => {
|
||||
// 根据当前路径匹配子应用
|
||||
const matched = subApps.find((app) =>
|
||||
route.path.startsWith(app.baseroute)
|
||||
)
|
||||
return matched || subApps[0] || { name: 'unknown', url: '', baseroute: '/' }
|
||||
})
|
||||
|
||||
// ============================================
|
||||
// micro-app 生命周期回调
|
||||
// ============================================
|
||||
|
||||
function onCreated() {
|
||||
console.log(`[ChildApp] 子应用 ${currentApp.value.name} 被创建`)
|
||||
}
|
||||
|
||||
function onBeforeMount() {
|
||||
console.log(`[ChildApp] 子应用 ${currentApp.value.name} 即将挂载`)
|
||||
}
|
||||
|
||||
function onMounted() {
|
||||
console.log(`[ChildApp] 子应用 ${currentApp.value.name} 挂载完成`)
|
||||
}
|
||||
|
||||
function onUnmount() {
|
||||
console.log(`[ChildApp] 子应用 ${currentApp.value.name} 已卸载`)
|
||||
}
|
||||
|
||||
function onError(error: Error) {
|
||||
console.error(`[ChildApp] 子应用 ${currentApp.value.name} 加载出错:`, error)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.child-app-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 确保 micro-app 标签占满容器 */
|
||||
.child-app-wrapper :deep(micro-app) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 加载占位 */
|
||||
.loading-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
min-height: 400px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border: 3px solid #e0e0e0;
|
||||
border-top-color: #667eea;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.loading-placeholder p {
|
||||
font-size: 14px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.loading-hint {
|
||||
font-size: 12px !important;
|
||||
color: #bbb;
|
||||
margin-top: 8px !important;
|
||||
}
|
||||
|
||||
.loading-hint code {
|
||||
background: #f0f0f0;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
167
src/views/Home.vue
Normal file
167
src/views/Home.vue
Normal file
@@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<div class="home-page">
|
||||
<div class="hero">
|
||||
<h2>欢迎使用 MicroApp 微前端主应用</h2>
|
||||
<p class="desc">
|
||||
基于 <code>@micro-zoe/micro-app</code> + Vue 3 + Vite 搭建
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="sub-app-list">
|
||||
<h3>已接入的子应用</h3>
|
||||
<div class="cards">
|
||||
<div
|
||||
v-for="app in subApps"
|
||||
:key="app.name"
|
||||
class="sub-app-card"
|
||||
@click="$router.push(app.baseroute)"
|
||||
>
|
||||
<div class="card-icon">📦</div>
|
||||
<div class="card-info">
|
||||
<h4>{{ app.name }}</h4>
|
||||
<p class="card-url">{{ app.url }}</p>
|
||||
<p class="card-route">路由前缀:{{ app.baseroute }}</p>
|
||||
</div>
|
||||
<div class="card-arrow">→</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="subApps.length === 0" class="empty">
|
||||
<p>暂未接入任何子应用</p>
|
||||
<p class="hint">请在 <code>src/config/subApps.ts</code> 中配置子应用信息</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { subApps } from '@/config/subApps'
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.home-page {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 40px 24px;
|
||||
}
|
||||
|
||||
.hero {
|
||||
text-align: center;
|
||||
padding: 48px 0 40px;
|
||||
}
|
||||
|
||||
.hero h2 {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.hero .desc {
|
||||
margin-top: 12px;
|
||||
color: #666;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.hero code {
|
||||
background: #e8e8e8;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.sub-app-list {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.sub-app-list h3 {
|
||||
font-size: 16px;
|
||||
color: #444;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.cards {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.sub-app-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.sub-app-card:hover {
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 4px 16px rgba(102, 126, 234, 0.15);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 36px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.card-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.card-info h4 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.card-url {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.card-route {
|
||||
font-size: 12px;
|
||||
color: #667eea;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.card-arrow {
|
||||
font-size: 20px;
|
||||
color: #ccc;
|
||||
flex-shrink: 0;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.sub-app-card:hover .card-arrow {
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.empty {
|
||||
text-align: center;
|
||||
padding: 48px 0;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.empty .hint {
|
||||
margin-top: 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.empty code {
|
||||
background: #e8e8e8;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
19
src/vite-env.d.ts
vendored
Normal file
19
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
|
||||
// micro-app 全局变量声明
|
||||
declare global {
|
||||
interface Window {
|
||||
__MICRO_APP_ENVIRONMENT__?: boolean
|
||||
__MICRO_APP_NAME__?: string
|
||||
__MICRO_APP_BASE_ROUTE__?: string
|
||||
microApp?: any
|
||||
}
|
||||
}
|
||||
|
||||
export {}
|
||||
Reference in New Issue
Block a user