## 你要什么
两个项目:
```
host_modele(主应用) — 外壳,路由到子应用
test_module(子应用) — 表格插件(纯展示组件)
```
## 子应用要改的文件
### 1. vue.config.js — 开跨域 + UMD
```js
module.exports = {
publicPath: '/test_entry/', // 子应用唯一标识
devServer: {
port: 8082, // 固定端口
headers: {
'Access-Control-Allow-Origin': '*', // qiankun 跨域
}
},
configureWebpack: {
output: {
libraryTarget: 'umd', // qiankun 用 UMD 加载
}
}
}
```
### 2. src/main.js — 导出 qiankun 生命周期
```js
import Vue from 'vue'
import App from './App.vue'
import createRouter from './router.js'
let instance = null
function render(props = {}) {
const { container, bus } = props
const router = createRouter()
if (bus) Vue.prototype.bus = bus // 接收主应用传来的总线
instance = new Vue({
router,
render: h => h(App)
}).$mount(container ? container.querySelector('#app') : '#app')
}
// 独立运行
if (!window.__POWERED_BY_QIANKUN__) {
render()
}
// qiankun 生命周期(三个必须)
export async function bootstrap() {}
export async function mount(props) { render(props) }
export async function unmount() {
instance.$destroy()
instance = null
}
```
### 3. src/App.vue — JS 设高度(防 qiankun bug)
```vue
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
mounted() {
// 向上遍历父链,跳过零高度包装层
const el = this.$el
let p = el.parentElement
while (p && p.clientHeight === 0) p = p.parentElement
if (p && p.clientHeight > 0) {
el.style.height = p.clientHeight + 'px'
}
}
}
</script>
```
参见 [[qiankun-height-100-percent-bug]]
### 4. 路由 — 指向插件组件
```js
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default () => new Router({
mode: 'history',
routes: [
{ path: '*', component: () => import('./views/PluginEntry.vue') }
]
})
```
### 5. PluginEntry.vue — 胶水层组件(重点)
胶水层负责三件事:收 Props、管数据、接事件。
```vue
<template>
<PureComponent v-bind="componentProps" @fetch="handleFetch" />
</template>
<script>
export default {
data() {
return {
componentData: [],
total: 0,
}
},
computed: {
pageParams() { return this.$root.pageParams || {} },
componentProps() {
return {
...(this.pageParams.Props || {}),
data: this.componentData,
total: this.total,
}
}
},
created() {
if (this.bus) {
this.bus.$on('table:data', ({ list, total }) => {
this.componentData = list
this.total = total
})
this.bus.$emit('table:fetch', { page: 1, row: 20 })
}
},
beforeDestroy() {
if (this.bus) this.bus.$off('table:data')
},
methods: {
handleFetch(params) {
if (this.bus) this.bus.$emit('table:fetch', params)
}
}
}
</script>
```
**为什么需要它:** TzxTable 是纯展示组件,不管数据从哪来。PluginEntry 接管数据和事件,让展示组件保持纯粹。[[tableentry-role-in-architecture]]
## 主应用要改的文件
### 1. App.vue — 放子应用容器
```vue
<template>
<div id="app" style="height:100vh;">
<div id="subapp-container" style="width:100%;height:100%;"></div>
</div>
</template>
```
### 2. main.js — 创建 bus、注册子应用
```js
import Vue from 'vue'
import axios from 'axios'
import { registerMicroApps, start } from 'qiankun'
// 创建事件总线(关键)
const bus = new Vue()
Vue.prototype.bus = bus
// 主应用监听数据请求,调后端
bus.$on('table:fetch', async (params) => {
try {
const res = await axios.get('/stations', { params })
if (res.data.status === 1) {
bus.$emit('table:data', {
list: res.data.data.list,
total: res.data.data.total
})
}
} catch (err) {
console.error('获取数据失败', err)
}
})
registerMicroApps([
{
name: 'my-plugin',
entry: '//localhost:8082/test_entry/',
container: '#subapp-container',
activeRule: '/test_entry/my-plugin',
props: {
bus,
params: {
Props: {
columns: [ /* 列配置 */ ],
loadMode: 'page',
pageSize: 20,
}
}
}
}
])
start()
```
## 通信流程
```
主应用
│ bus = new Vue()
│ bus.$on('table:fetch') → axios → bus.$emit('table:data')
│ 注册子应用时传 bus + Props
│
└→ qiankun 加载
│
└→ PluginEntry
│ Props + bus → 传给 PureComponent
│ 翻页 → bus.$emit('table:fetch')
│ bus.$on('table:data') → 更新数据
│
└→ PureComponent
只管渲染
```
## 开发生命周期
| 顺序 | 文件 | 注意 |
|------|------|------|
| 1 | vue.config.js | 跨域头 + UMD |
| 2 | main.js(子) | 三个生命周期 + bus |
| 3 | App.vue(子) | mounted 设高度 |
| 4 | PluginEntry.vue | 胶水层模板 |
| 5 | App.vue(主) | 容器 div |
| 6 | main.js(主) | bus + 注册 |
**How to apply:** 新建 qiankun 子应用项目时,按上述 6 步配置即可。核心记住:**子应用只展示,主应用只调接口,bus 在中间传话。**