MicroApp样例搭建【Vue】

样例代码可以点击这里下载,搭建流程如下

项目创建

项目由 vue 的官方脚手架创建,基项目 base 和子项目 app_first、app_second

1
2
3
4
vue create base

vue create app_first
vue create app_second

基项目修改

添加本地运行配置文件 vue.config.js

1
2
3
4
5
6
module.exports = {
devServer: {
host: 'localhost'
, port: 3000
}
}

安装 micro-app 插件

1
npm install @micro-zoe/micro-app --save

添加 micro/index.js 文件

1
2
3
4
5
6
7
8
import microApp from '@micro-zoe/micro-app'
import * as config from './config'

// 启用 micro
microApp.start({
preFetchApps: config.MICRO_APPS //加载子项目
, globalAssets: config.GLOBAL_ASSETS // 加载全局资源
})

添加 micro/config.js 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 子应用前缀
export const CHILD_PREFIX = 'app'

// 子应用地址
export const MICRO_APPS = [
{ name: 'first-child', url: `http://localhost:3001/` }
, { name: 'second-child', url: `http://localhost:3002/` }
]

// 全局资源
export const GLOBAL_ASSETS = {
js: []
, css: []
}

修改入口文件 main.js,引入 micro-app 配置

1
2
import './micro'
...

修改 App.vue 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<template>
<div>
<!--路由链接-->
...
</div>
<div>
<micro-app
v-if="isChild"
v-bind="micro"
destory
@datachange='handleDataChange'></micro-app>
<router-view v-else></router-view>
</div>
</template>

<script>
import { MICRO_APPS, CHILD_PREFIX } from './micro/config.js'

export default {
...
, data(){
return {
...
, isChild: false // 是否是子模块
, micro: {
url: '' // 子应用地址,不包含路由地址
, key: '' // Vue 标签的 key 值,用于不同子模块间的切换时,组件重新渲染
, name: '' // 子模块名称,值唯一
, data: {} // 需要传入子模块的数据
, baseroute: '' // 子模块路由地址
}
}
}
, watch: {
$route (val){
// 监听路由变化,改变子模块渲染
this.changeChild(val)
}
}
, created () {
this.changeChild(this.$route)
}
, methods: {
...
, getAppUrl (name) { // 获取子模块 url 和 name
return MICRO_APPS.find(app => app.name === name) || {}
}
, changeChild (route) { // 修改子视图显示
let path = route.path.toLowerCase()
, paths = path.split('/')

// 判断是否为子模块,子模块有固定的前缀
this.isChild = paths.length > 2 && paths[1] === CHILD_PREFIX

if (this.isChild) {
let app = this.getAppUrl(paths[2])

this.micro = {
...app
, data: { name: route.name } // 根据路由和子项目跳转方式,传入对应参数
, key: `${app.name}`
, baseroute: `/${CHILD_PREFIX}/${paths[2]}`
}
}
}
, handleDataChange (event) { // 获取子路由传递的信息
let data = event.detail.data
if(data.route) this.$router.push({ name: data.route.name })
}
}
}
</script>

修改路由文件 router/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
...
import { CHILD_PREFIX } from '@/micro/config.js'

const routes = [
...
, { // app_first 项目路由
path: `/${CHILD_PREFIX}/first-child`
, name: 'FirstChild'
, children: [
{
path: 'home'
, name: 'FirstHome'
}
, {
path: 'about'
, name: 'FirstAbout'
}
]
}
, { // app_second 项目路由
path: `/${CHILD_PREFIX}/second-child`
, name: 'SecondChild'
, children: [
{
path: 'home'
, name: 'SecondHome'
}
, {
path: 'about'
, name: 'SecondAbout'
}
]
}
]

...

子项目修改

添加本地运行配置文件 vue.config.js,设置允许跨域访问

1
2
3
4
5
6
7
8
9
module.exports = {
devServer: {
host: 'localhost'
, port: 3001
, headers: { // 设置本地运行的跨域权限
'Access-Control-Allow-Origin': '*',
}
}
}

添加 micro/index.js 文件

1
2
// 设置 webpack 的公共路径
__webpack_public_path__ = window.__MICRO_APP_PUBLIC_PATH__ || '/'

入口文件修改 mian.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import './micro'
...

let app

/**
* 挂载函数
*/
function mount () {
app = new Vue({
el: '#app',
router,
render: function (h) { return h(App) }
})
}

/**
* 卸载函数
*/
function unmount () {
app.$destroy()
app.$el.innerHTML = ''
app = null
}

// 微前端环境下,注册mount和unmount方法
if (window.__MICRO_APP_ENVIRONMENT__)
window[`micro-app-${window.__MICRO_APP_NAME__}`] = { mount, unmount }
else
mount()

修改 App.vue 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<template>
<div id="app">
<div id="nav">
<router-link :to="`${prefix}/home`">Home</router-link> |
<router-link :to="`${prefix}/about`">About</router-link> |
<button @click="goto('SecondHome')">SecondHome</button> |
<button @click="goto('SecondAbout')">SecondAbout</button>
</div>
<router-view />
</div>
</template>

<script>
export default {
...
, methods: {
...
, dataListener (data) {
// 不判断时会报一个“冗余导航【NavigationDuplicated】”的异常
if (data.name !== this.$route.name)
this.$router.push({ name: data.name })
}
, goto (name) {
// 向基项目发送数据
window.microApp.dispatch({ route: { name } })
}
}
, created () {
// 绑定数据【data属性】监听事件
window.microApp && window.microApp.addDataListener(this.dataListener)
}
, destroyed () {
// 移除数据【data属性】监听事件
window.microApp && window.microApp.removeDataListener(this.dataListener)
}
}
</script>

修改路由文件 router/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
...
const routes = [
{
path: window.__MICRO_APP_BASE_ROUTE__ || '/' // 根据项目运行的不同环境,设置路径的前缀
, name: 'Home'
, redirect: { name: 'FirstHome' }
, component: () => import('../views/Empty.vue') // Empty.vue 是一个只包含 router-view的页面,用于渲染 children
, children: [
...
]
}
]
...

项目启动

分别进入项目文件夹,在 CMD 下运行 npm run serve,启动项目,访问 localhost:3000 查看完整的项目,也可以打开 localhost:3001 或者 localhost:3002 访问单个子项目

GIF 2022-2-11 10-53-03

一些问题处理

  • 公共模块代码

    可以将公共代码单独创建一个仓库,在各个项目中以子模块的形式引入到项目中

  • Nginx 部署

    微前端在部署到多个地址时,可以通过 nginx 的反向代理,把子项目的地址代理到主项目地址下,这样可以是主项目和子项目使用相同的 storage 存储

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    server {
    listen 3000;
    server_name localhost;

    location / {
    root D:/base/dist/;
    index index.html index.htm;
    try_files $uri $uri/ /index.html;
    }

    # 代理子项目
    location /first_child/ {
    proxy_pass http://localhost:3001/;
    }
    }

    # 子项目访问端口
    server {
    listen 3001;
    server_name localhost;

    # 设置跨域访问权限
    add_header Access-Control-Allow-Origin *;

    location / {
    root D:/first_child/dist/;
    index index.html index.htm;
    try_files $uri $uri/ /index.html;
    }
    }
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

扫一扫,分享到微信

微信分享二维码

~