背景

最近在对公司node+jquery捞系统基层进行版本升级,使用最新技术栈vue + ts + vite + pinia,对于捞系统原有页面将会进行iframe不会再其基础上进行修改,大大减少了维护成本,但是由于其页面较多,且页面之间存在关联,所以需要对页面进行缓存,避免重复加载,提升用户体验,其中也遇到了一系列兼容问题,总结如下:

对于系统升级,当然要使用最新技术栈了,这样公司使用的技术栈就会统一,也不会因为技术栈不统一导致个人成长缓慢;对于相对系列问题也可以进行小组探讨;

从计划开始部署到真正上手改造花费将近一周时间,底层框架结构采用vue,里层内容采用iframe链接老项目页面;

Start

开始进行对系统技术的升级,大致升级的范围:

  • 登录,重置密码重构,摒弃原来jquery页面
  • 路由,权限的重置,需要后端开发人员配合,现在市面上的中后台系统的路由和权限基本都是后端来管理,很少使用前端路由
  • 基础库,方法的编写,对于常用的方法,hooks需要统一封装管理
  • 常用枚举的封装,接口定义,类型定义
  • 请求拦截,相应的封装;

环境的配置

当然对于企业项目,多环境已经是常见的,开发环境,测试环境,预发布环境,线上环境等等;对于这些环境一些请求的域名,配置参数当然也是不一样的,这时候就可以在.env文件中配置,大概配置项如下:

1
2
3
4
5
6
VITE_APP_API_URL='http://www.wangxiaoze.wang'
VITE_APP_API_KEY='abc'
VITE_APP_APP_ID='123456'

# 当然对于iframe页面需要相对环境的域名配置
VITE_APP_IFRAME_HOST='http://www.wangxiaoze.wang'

utils, hooks 等基础配置的封装

  1. utils基础的方法,正则的封装等等;
  2. hooks的封装,对于一些常用的方法,比如loadingmessagedialogconfirmloading等方法,需要统一封装管理;
  3. config对一些系统级的配置项,如:七牛的配置,标题,logo 的配置等等
  4. store-pinia的配置
  5. axios的封装,对于请求的拦截,响应的封装,请求的封装等等
  6. api的封装,对于一些接口的封装,如:七牛的图片上传,七牛的图片删除,七牛的图片列表等等

axios, api 的封装

对于axios的使用,代码如下:

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
const instance: AxiosInstance = axios.create({
baseURL: `${import.meta.env.VITE_APP_BASE_URL}`,
timeout: 60000,
});

// 请求拦截器
instance.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
// 根据自己的需要进行修改
return config;
},
error => error
);

// 响应拦截器
instance.interceptors.response.use(
(response: AxiosResponse) => {
// 根据自己的需要进行修改
return response.data;
},
error => {
return Promise.reject(error.response);
}
);

interface IHttpResult<T> {
code: number;
message: string;
data: T;
success: boolean;
}

// 泛型T: 返回接口字段类型 泛型U: 是参数的数据类型
const httpServer = {
get<T = any, U = any>(url: string, params: U): Promise<IHttpResult<T>> {
return instance.request(getParamsConfig(url, "get", params));
},
post<T = any, U = any>(url: string, params: U): Promise<IHttpResult<T>> {
return instance.request(getParamsConfig(url, "post", params));
},
put<T = any, U = any>(url: string, params: U): Promise<IHttpResult<T>> {
return instance.request(getParamsConfig(url, "put", params));
},
delete<T = any, U = any>(url: string, params: U): Promise<IHttpResult<T>> {
return instance.request(getParamsConfig(url, "delete", params));
},
postForQuery<T = any, U = any>(
url: string,
params: U
): Promise<IHttpResult<T>> {
return instance.request(
getParamsConfig(
url,
"post",
qs.stringify(params, {
arrayFormat: "repeat",
})
)
);
},
};

export { instance as axios, httpServer };

系统采用的是ts,那么对于api的管理使用枚举整理,相关api的配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum UsetApi {
// 七牛图片上传
UploadImage = "/api/upload/image",
// 七牛图片删除
DeleteImage = "/api/upload/delete",
// 七牛图片列表
ImageList = "/api/upload/list",
}

import { httpServer } from "@/http";

export function getList<T, U>(data: U) {
return httpServer.get<T>(UsetApi.ImageList, data);
}

iframe 内嵌页面缓存

刚开始使用iframe会发现一个问题,就是iframe内嵌页面刷新后,iframe会重新加载,导致页面会重新刷新,iframe 资源也会重新加载,会造成性能影响 那么如何解决呢?

  • 使用v-show这样就可以避免刷新资源的问题
  • 定位将 iframe 页面定位可视区域之外;
1
2
3
4
5
<iframe
v-for="item in routes"
:key="item.path"
v-show="item.path === route.path"
/>

虽然发现不会再次请求资源,刷新页面,但是会发现一个细节问题,那就是滚动条不会定位到上次的位置,这样用户频繁跳转页面,滚动条不会定位,会造成一定的使用不便;

另外的一中方案可以解决此类问题;

老系统的预览图片,打开标签页兼容

捞系统的预览图片会打开新的标签页,但是新系统要使用element-plus的预览图片的样式,当然打开标签页也是同样问题;

想要同时解决新老系统的兼容问题,那么可以参考一下方案:

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
// 老系统
window.parent.postMessage(
{
command: "open",
data: {
url: url,
},
},
"*"
);

// 新系统
window.addEventListener("message", function (e) {
if (e.data.command === "open") {
// 根据自己的情况操作逻辑
// 判断是否是当前页面打开
}
});

// 移除,避免叠加
window.removeEventListener("message", function (e) {
if (e.data.command === "open") {
// 根据自己的情况操作逻辑
// 判断是否是当前页面打开
}
});

当然图片或者其他也是类似操作;