# 旧应用改造手册
本教程针对旧项目改造微应用使用,主要目的将现有旧项目改造为微应用模式。
📌tips:
- 除了将旧项目改造为微应用外,也可以进行反向操作。下载平台子应用脚手架,将旧项目业务代码进行迁移以完成改造。
# 步骤
- 下载平台主应用脚手架 glink-project-web (opens new window)
- 将旧项目进行子应用改造
- 完成项目对接
# 环境准备
- 平台集成开发工具包: @adam/micro
- 平台提供组件库可选:@adam/glink
# 安装开发工具库
使用 npm 的方式安装,它能更好地和 webpack 打包工具配合使用。
@adam/micro 提供了微应用数据共享,样式隔离,路由转化等功能。
注意:安装之前要确保已把 npm registry 设置为公司内网地址,如: registry=http://nexus.gosp.glkyun.com/repository/npm-all/。另外此包需要结
合webpack配置使用
npm install @adam/micro
# 使用说明
微前端全局状态管理类由原生JS编写,技术栈无关,因此可与任何前端框架配合使用 针对Vue应用提供了一些实用的插件,其它框架须自行实现,下面的介绍也会以Vue应用为主
# 一、微应用集成说明
主要包括:
- 在微应用的入口文件中集成
- 打包输出UMD模块
- 平台路由
- 样式隔离支持
- 统一数据管理
# 1. 在微应用中的入口文件中集成
环境变量配置
# 系统名称,package.json中的name尽量与其保持一致
VUE_APP_NAME='glink-system-manage'
# 部署路径名称,同时为ngxin代理路径名称
VUE_APP_PUBLIC_PATH = 'glink-system-manage'
# 路由统一前缀,用于匹配当前微应用
VUE_APP_ROUTER_BASE = '/glink-system-manage'
2
3
4
5
6
微前端全局状态集成,根据状态在XHR请求头中设置上下文信息、动态设置当前语言等
导出微前端生命周期勾子
平台门户重要路径参数保持,如:当前组织
新建public-path.js文件
if (window.__POWERED_BY_QIANKUN__) {
// 动态设置 webpack publicPath,防止资源加载出错
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
2
3
4
5
更改入口文件
import Vue from 'vue'
import App from './App.vue'
import routesModule from './router'
import store from '@common/store'
import '@/plugins'
import '@/directives'
import '@/utils/jsonpCallBack'
import '@/style'
import init from '@common/utils/init'
import '@/utils/error'
import * as request from '@business/utils/request'
import 'core-js/stable'
import 'regenerator-runtime/runtime'
import Es6Promise from 'es6-promise'
import config from './config.json'
Es6Promise.polyfill()
// 以上引入为示例,可选部分需要添加
// 微前端
import VueRouter from "vue-router";
import "./public-path";
import { resetRouter } from '@adam/micro/src/utils/reset-router'
import { globalRegister } from '@adam/micro'
Vue.config.productionTip = false
// 初始化信息
init({
store,
config,
request,
routesModule
})
let instance = null;
let router = null;
let routes = routesModule.options.routes //为当前项目下所有路由,hash模式需要进行路由前缀统一处理
/**
* 渲染函数
* 两种情况:主应用生命周期钩子中运行 / 微应用单独启动时运行
*/
function render(props = {}) {
let { container, routerBase, mode } = props
routerBase = process.env.VUE_APP_ROUTER_BASE
// hash模式需要重新设置路由地址,增加前缀
if (mode === 'hash') {
resetRouter(routes,routerBase ,mode)
}
// 在 render 中创建 VueRouter,可以保证在卸载微应用时,移除 location 事件监听,防止事件污染
router = new VueRouter({
// 运行在主应用中时,添加路由命名空间
base: window.__POWERED_BY_QIANKUN__ ? routerBase : process.env.VUE_APP_ROUTER_BASE,
mode: mode?mode:'hash',
routes
});
// 监听路由
router.beforeEach((to, from, next) => {
if (window.__POWERED_BY_QIANKUN__) {
// 判断basePath,存在则是hash模式,需要变更路径
if (from.meta.basePath && !to.path.includes(from.meta.basePath) && to.path.indexOf('/') === 0) {
next({
path: from.meta.basePath + to.path,
query: to.query,
params: to.params
})
return
}
}
next()
})
// 挂载应用
instance = new Vue({
router,
store,
render: (h) => h(App),
}).$mount(container ? container.querySelector(`#app`) : `#app`)
}
// 独立运行时,直接挂载应用
if (!window.__POWERED_BY_QIANKUN__) {
globalRegister(store)
render()
}
/**
* bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
export async function bootstrap() {
console.log("brokenSubApp bootstraped");
}
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export async function mount(props) {
globalRegister(store,props)
console.log("brokenSubApp mount", props);
render(props);
}
/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
export async function unmount() {
console.log("brokenSubApp unmount");
instance.$destroy();
instance = null;
router = null;
}
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# 2. 打包输出UMD模块
如果使用 webpack 进行构建打包,可参考以下vue-cli配置片段
// vue.config.js
const packageName = require('./package.json').name
module.exports = {
// ...
publicPath: process.env.VUE_APP_PUBLIC_PATH ? `/${process.env.VUE_APP_PUBLIC_PATH}/` : '/',//生产环境取VUE_APP_PUBLIC_PATH
configureWebpack: {
output: {
library: `${packageName}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${packageName}`
}
}
}
if(process.env.NODE_ENV === 'development'){
vueConfigs.publicPath = '/',
// 配置跨域请求头,解决开发环境的跨域问题
headers: {
'Access-Control-Allow-Origin': '*'
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 3.样式隔离
基于postcss开发的样式隔离插件,用于隔离各系统之间的样式
项目根目录下新建postcss.config.js
const postcssiso = require('@adam/micro/src/utils/postcssiso')
module.exports = () => {
return {
plugins: [
require('autoprefixer')(),
postcssiso(`.${process.env.VUE_APP_NAME}`, {
ignore: ['button', 'input', 'span'] //可自定义忽略标签
})
]
}
}
2
3
4
5
6
7
8
9
10
11
在App.vue添加隔离标签:class="id"
<template>
<div id="app" :class="id" >
<router-view />
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
id: process.env.VUE_APP_NAME
}
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 4.数据共享
一部分动态唯一参数会存储到sessionStorage中,如当前产品编码,productCode:PRODUCT_CODE 主应用在注册时会统一将一部分通用参数,存储到子应用的store/global模块中 目前支持参数包括
* @param {boolean} invalidToken // token是否失效了
使用示例,如子应用token过期需要通知主应用:
this.$store.dispatch('global/setGlobalState', {invalidToken:true})
其他参数均可按vuex规范取出使用。
# 二、其他对接调整
旧项目改造还需要一些其他适配调整
# 子路由跳转
修改utils/router.js
const original = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
// 统一拼接子应用前缀(此处添加)
location.path = process.env.VUE_APP_ROUTER_BASE + location.path
return original.call(this, location).catch(err => err)
}
...
2
3
4
5
6
7
# basis服务移除
如果使用了原平台的basis服务请进行移除;由于服务已全部迁移至saas,原basis服务不可用,需进行移除 查看common/global.const.js 使用到basis的url请求服务全部进行移除
# 修改request函数的请求头
打开文件utils/request.js
...
config.headers = {
...headers,
'x-nepoch-org': store.state.auth.userInfo.deptId,
'x-nepoch-system': 12305,
'x-nepoch-application': system,
'x-nepoch-tenant': tenant?tenant.id: '1527589324236275712',
}
...
2
3
4
5
6
7
8
9
# 微应用模式路由修改
路由需去除嵌套路layout,layout由主应用提供 eg:
export default [
{
path: '/home',
component: () => import('@business/views/home'),
meta: {
title: '首页'
}
}
]
2
3
4
5
6
7
8
9