winstack 项目改造
背景:winstack项目由多项目组合起来,如kv、winstore、安全中心、存储、运维、网络、系统多个模块,像kv项目使用 angular技术, winstore、安全中心 使用 vue技术
kv项目是一个多页面angular 老项目,如果要改造微应用,相对比较麻烦,需要改动地方比较多,所以鉴于以上问题。就决定用微应用去加载iframe kv项目,如点击虚拟化资源菜单先去加载app-vue-kv应用,iframe会加载kv 指定路径url。
不要对
iframe
抱有偏见,它也是微前端的一种实现方式,如果页面上无弹窗、无全屏等操作,iframe
也是很好用的。配置缓存和cdn
加速,如果是内网访问,也不会很慢。
iframe
和qiankun
可以并存,jQuery
多页应用使用iframe
接入就挺好,什么时候什么场景该用哪种方案,具体情况具体分析。
微前端框架面临的两大共性问题
实际上所有的微前端框架都面临这两大共性问题。当你解决了这两大问题之后,你的微前端框架的运行时,就已经基本可用了。
- 问题一是应用的加载与切换。包括路由的处理、应用加载的处理和应用入口的选择。
- 问题二是应用的隔离与通信。这是应用已经加载之后面临的问题,它们包括 JS 的隔离(也就是副作用的隔离)、样式的隔离、也包括父子应用和子子应用之间的通信问题。
应用路由与 winstack 路由系统
首先我们来看应用路由的问题。在微前端体系结构下,路由的划分往往是如图这个样子的,这也是一种比较简单的方案。
我们主应用加载了应用 app-vue-kv 和应用 winstore。这个时候对于主应用来讲,我就有两个路由 /app-vue-kv/*
和 /winstore/*
。在主应用 路由 A 下我会加载应用 app-vue-kv
,在主应用 另一个路由 B 下加载应用winstore
。
我们需要通过点击主应用左侧菜单跳转加载应用,所以面临这问题后,去官网找答案,所以凡是遇事不要慌的。
如何在主应用的某个路由页面加载微应用
主应用路由设置
components.router.js
path:"/app-vue-kv"
{
path: "/app-vue-kv",
component: Layout,
name: "Compute",
meta: {
title: "计算",
id: "57064191-4fc8-47f7-afad-1b892397b2bd"
},
// redirect: "/compute/virtualizedResources",
children: [
{
path: "virtualizedResources",
component: () =>
import(
/* webpackChunkName: "virtualizedResources" */ "@/views/compute/VirtualizedResources.vue"
),
name: "VirtualizedResources",
meta: {
title: "云资源池",
icon: "virtualizedResources",
id: "4d302dd0-f831-4c31-b140-183ce98aa670",
needLogin: true,
noCache: false
}
}
]
},
index.router.js
注册这个路由时给 path
加一个 *
,注意:如果这个路由有其他子路由,需要另外注册一个路由,仍然使用这个组件即可。
// 在主应用的某个路由页面加载微应用
{
path: "/app-vue-kv",
name: "appVuekv",
component: Layout,
children: [
{
path: "*",
component: () => import("../views/Portal.vue"),
name: "Home",
meta: {
title: "总览",
icon: "home",
id: "00444",
needLogin: true
}
}
]
},
微应用的 activeRule 需要包含主应用的这个路由 path
const getActiveRule = hash => location => location.hash.startsWith(hash);
const microApps = [
{
name: "app-vue-kv",
entry: isProd ? "/child/app-vue-kv/" : "//localhost:9528",
activeRule: getActiveRule("#/app-vue-kv"),
props: { data: { store } }
}
];
在 Portal.vue
这个组件的 mounted
周期调用 start
函数,注意不要重复调用。
import { start } from 'qiankun';
export default {
mounted() {
if (!window.qiankunStarted) {
window.qiankunStarted = true;
start();
}
},
}
子应用app-vue-kv 如是hash模式 router 如何配置
const routes = [
{
path: "/app-vue-kv",
name: "Home",
component: Home,
children: [
// 其他的路由都写到这里
{
path: "virtualizedResources",
name: "VirtualizedResources",
component: () =>
import(
/* webpackChunkName: "virtualizedResources" */ "../views/VirtualizedResources.vue"
)
}
]
}
];
子应用winstore 如是hash模式 router 如何配置
{
path: "/winstore/resource",
component: Layout,
name: "Resource",
meta: {
title: "资源管理",
id: "fa1aa081-bc7d-4c0c-9daf-4ac497007813"
},
children: [
{
path: "storagePoolManagement",
component: () =>
import(
/* webpackChunkName: "StoragePoolManagement" */
"@/views/resource/StoragePoolManagement.vue"
),
name: "StoragePoolManagement",
meta: {
title: "存储池管理",
icon: "storagePoolManagement",
id: "f6bfd3a8-cc7c-4e17-9ab3-04e7fe69815c",
needLogin: true,
noCache: false
}
}
]
},
子应用路由跳转 尽量使用name this.$router.push({ name: "WinstoreLogDetail" });
微应用数据请求跨域配置
背景
当前存在以vue2.6 开发的单例应用, 我们希望使用qiankun
,将该应用转微应用结构。
这里记录使用vue做微应用开发时,各个应用数据请求代理的配置方式.
root
└── child/ # 存放所有微应用的文件夹
| ├── app-vue-kv/ # 存放微应用 app-vue-kv 的文件夹
| ├── app-vue-login/ # 存放微应用 app-vue-login 的文件夹
| ├── securityCenter/ # 存放微应用 securityCenter 的文件夹
| ├── winstore/ # 存放微应用 winstore 的文件夹
└── main/ # 主应用
问题
我们知道一般,独立使用vue开发单例应用,可以通过配置 vue.config.js
的{ devServer: proxy }
实现接口请求代理.
而在微应用开发中,情况稍有不同。 具体的可以分为以下几种情况:
- 基座独立开发代理
- 子应用独立开发代理
- 子应用嵌套基座内的代理
现在的问题是,当基座和子应用各自独立配置代理服务时,各自独立开发请求数据是正常的。
而子应用加载到基座后,子应用的数据请求将为404
。
基座代理
基座版本vue2.6.12
,
devServer: {
proxy: {
"/api": {
//此处要与 /services/api.js 中的 API_PROXY_PREFIX 值保持一致
// target: "http://192.168.205.213:8080/",
target: userDev.api,
changeOrigin: true, //是否跨域
pathRewrite: {
"^/api": ""
},
ws: false
},
"/websocket": {
target: userDev.socket, // 为目标变量
changeOrigin: true, //是否跨域
ws: false, // ws: false 解决sockjs 一直在频繁发送这个请求
pathRewrite: {
"^/websocket": "/websocket"
}
}
},
},
子应用代理
devServer: {
headers: {
"Access-Control-Allow-Origin": "*"
},
proxy: {
"/api": {
//此处要与 /services/api.js 中的 API_PROXY_PREFIX 值保持一致
// target: "http://192.168.205.213:8080/",
target: userDev.api,
changeOrigin: true, //是否跨域
pathRewrite: {
"^/api": ""
},
ws: false
},
"/websocket": {
target: userDev.socket, // 为目标变量
changeOrigin: true, //是否跨域
ws: false, // ws: false 解决sockjs 一直在频繁发送这个请求
pathRewrite: {
"^/websocket": "/websocket"
}
}
},
},
分析
初看这里微应用中的代理配置和单例配置没有任何区别.其实代理的配置基础都是基于vue.config.js配置或者说是 webpack代理配置, 之所以 这里子应用无论独立开发或加载到基座联调,都能正常请求到数据,是因为基座和子应用都配置了相同的代理头 /proxyApi
并且请求的服务地址一致
这里回到问题, 如果基座和子应用独立配置, 例如:
子应用独立开发时的请求地址: /user
当子应用嵌套在基座内时,地址将变为: localhost: 9000/user
可以看到请求地址发生了变换,原/user
地址在独立开发时是能通过webpack server 做正常代理的.
而在微服务中, /user
地址转向了基座的本地开发服务 localhost:9000/user
而开发服务是无法识别这个请求地址的,自然返回404
所以我们将子应用的代理头与基座同步, 这样子应用的请求将通过基座的开发服务做代理转发, 之所以设置相同的代理头而不是在基座再配置一遍子应该代理,也是为了方便设置,遵循约定大于配置
原则。
如何部署
官网提供方案:qiankun.umijs.org/zh/cookbook…
采用方案1:微应用都放在在一个特殊名称(不会和微应用重名)的文件夹下(建议使用)
└── dist/ # 根文件夹 (打包后如下放置)
|
├── child/ # 存放所有微应用的文件夹
| ├── app-vue-kv/ # 存放微应用 app-vue-kv 的文件夹
| ├── app-vue-login/ # 存放微应用 app-vue-login 的文件夹
| ├── securityCenter/ # 存放微应用 securityCenter 的文件夹
| ├── winstore/ # 存放微应用 winstore 的文件夹
├── index.html # 主应用的index.html
├── css/ # 主应用的css文件夹
├── js/ # 主应用的js文件夹
└── common/ # 公共文件
|
├── src/
└── child/ # 存放所有微应用的文件夹
| ├── app-vue-kv/ # 存放微应用 app-vue-kv 的文件夹
| ├── app-vue-login/ # 存放微应用 app-vue-login 的文件夹
| ├── securityCenter/ # 存放微应用 securityCenter 的文件夹
| ├── winstore/ # 存放微应用 winstore 的文件夹
└── main/ # 主应用
因为main、子应用都是采用hash模式。 所以不需要设置路由 base
main主应用 micro-app.js
import store from "./store";
import router from "./router";
const getActiveRule = hash => location => location.hash.startsWith(hash);
/**
* 注意name应与子项目的package里的name字段保持一致,
* activeRule后面的地址与name保持一致。
* 这个地址不管在生产环境还是开发环境,访问子项目时的地址都不会变的,
* 但是子项目的访问地址entry会随环境变化。
* 比如本例vue子项目在开发环境地址是//localhost:9530,生产环境是/child/winstore/
*/
const isProd = process.env.NODE_ENV === "production";
const microApps = [
{
name: "securityCenter",
entry: isProd ? "/child/securityCenter/" : "//localhost:9531",
activeRule: getActiveRule("#/securityCenter")
},
{
name: "winstore",
entry: isProd ? "/child/winstore/" : "//localhost:9530",
activeRule: getActiveRule("#/winstore"),
props: { data: { store } }
},
{
name: "app-vue-login",
entry: isProd ? "/child/app-vue-login/" : "//localhost:9529",
activeRule: getActiveRule("#/app-vue-login"),
props: { data: { store, router } }
},
{
name: "app-vue-kv",
entry: isProd ? "/child/app-vue-kv/" : "//localhost:9528",
activeRule: getActiveRule("#/app-vue-kv"),
props: { data: { store } }
}
];
// 处理组件共享
const commonComponents = {};
const apps = microApps.map(item => {
return {
...item,
container: "#appContainer" // 子应用挂载的div
};
});
export default apps;
process.env.NODE_ENV === "production" 区分开发环境
- 如是development时,直接使用本地开发环境
- 如是production时,需要打包
主应用和微应用部署到同一个服务器(同一个IP和端口)
在根目录下:
先安装依赖: `npm install`,再执行`npm run install-all`为所有项目安装依赖,最后执行`npm run start-all`即可启动所有的项目。
`npm run build-all`可以打包所有`vue`项目,`jQuery`项目不需要打包。
子应用 vue.config.js 设置publicPath
module.exports = {
publicPath: process.env.NODE_ENV === "production" ? "/child/winstore/" : "/",
}
nginx
后端部署配置
server {
listen 8080;
server_name localhost;
location "" {
#alias /opt/winsphere/webapp;
alias /opt/winsphere/web/dist;
index index.html;
}
location /child/webapp {
alias /opt/winsphere/webapp;
index index.html;
}
location /child/app-vue-login {
alias /opt/winsphere/web/dist/child/app-vue-login;
index index.html;
}
location /child/app-vue-kv {
alias /opt/winsphere/web/dist/child/app-vue-kv;
index index.html;
}
location /child/securityCenter {
alias /opt/winsphere/web/dist/child/securityCenter;
index index.html;
}
location /child/winstore {
alias /opt/winsphere/web/dist/child/winstore;
index index.html;
}
}
结尾
在对winstack 遇到路由跳转问题、打包问题、跨域问题等。。可能不会像vue使用那么6哦,需要踩坑的,遇到这些问题,可以看官网、github issues ,实在解决不了的问题,可以在钉钉找队友解决,开发下来,这些问题都通过这些方式可以解决的。