前端性能优化之重构方案和计划 - zptime/blog GitHub Wiki
随着项目的敏捷开发快速迭代,相似的代码越来越多,开发时可能为了赶进度,没那么多时间考虑代码质量,业务流程也随着迭代不断扩展,开发时没法考虑的那么全面。最终,前端项目将会愈发冗余,且存在诸多历史遗留代码或者问题。所以隔一段时间,进行适度的代码重构是很必要的,可提高代码的可读性和代码质量。但重构也不是说必须一下全部搞完,也需要考虑重构的代价,特别是一些运行很久的代码,里面肯定有很多踩坑的过程,要综合考虑重构是否值得以及重构的范围。对于自己完全熟悉的代码,也要一步一步来,避免影响正常的项目迭代。
业务逻辑重构
主要是优化代码结构,提高可读性;进行合理的拆分和组合,提高代码复用性
- 排查并去除程序中的魔鬼数字,将数字定义为具有实际意义的宏常量
- 排查并使用卫语句解除 if 语句的多层嵌套
- 排查并删除冗余的业务逻辑
- 排查并合并重复业务代码,抽离出公共函数
- 排查业务逻辑和底层工具类控件类是否存在互相依赖耦合
- 排查并将后台提供的网络接口封装为 API,实现前端业务界面和后端 API 分离
- 排查过长大函数,将大函数分成若干独立功能小函数,避免一个函数实现过多功能,遵循“单一职责原则”
- 用多态替换条件判断
以下函数举例,存在上述中的 1,2 两种情况
// 改造前
// (1) 1,2,3等都是魔鬼数字
// (2) 存在if/else多层嵌套
function doSomething() {
if (this.type === 1) {
// 编辑部分数据
// todo...
} else {
// todo...
let msg = "";
if (this.type === 2) {
msg = "编辑成功";
// todo...
} else if (this.type === 3) {
msg = "新增成功";
// todo
}
// todo...
}
}
// 改造后
const ADD_FLAG = 1;
const EDIT_FLAG = 2;
const UPDATE_FLAG = 3;
function doSomething() {
switch (this.type) {
case "UPDATE_FLAG":
// todo
break;
case "EDIT_FLAG":
let msg = "编辑成功";
// todo
case "ADD_FLAG":
let msg = "新增成功";
// todo
default:
// todo
break;
}
}
以下函数举例,存在上述中的 7 的情况
// 改造前
function submitForm() {
if (this.type === "part") {
// 编辑部分数据
let params = {
id: this.id,
memberIds: R.map((o) => o.id, this.members),
};
Api["modifyPartConfig"](params).then((res) => {
this.$notification.success({
class: "m-notice",
message: "配置成功",
});
});
} else {
// 编辑全量数据
this.$refs["formRule"].submitForm((data) => {
if (!data) return;
let params = R.mergeDeepRight(this.formInfo, data);
params.memberIds = R.map((o) => o.id, this.members);
let msg = "";
if (this.type === "edit") {
params["id"] = this.id;
msg = "编辑成功";
} else {
msg = "新增成功";
params.configRequest = R.clone(params.config);
}
Api["modifyConfig"](params).then((res) => {
this.$notification.success({
class: "m-notice",
message: msg,
});
});
});
}
}
// 改造后
function submitForm() {
if (this.type === "part") {
// 编辑部分数据
let params = {
id: this.id,
memberIds: R.map((o) => o.id, this.members),
};
Api["modifyPartConfig"](params).then((res) => {
this.$notification.success({
class: "m-notice",
message: "配置成功",
});
});
return false;
}
this.handleEditSubmit();
}
function handleEditSubmit() {
// 编辑全量数据
this.$refs["formRule"].submitForm((data) => {
if (!data) return;
let params = R.mergeDeepRight(this.formInfo, data);
params.memberIds = R.map((o) => o.id, this.members);
let msg = "";
if (this.type === "edit") {
params.id = this.id;
msg = "编辑成功";
} else {
msg = "新增成功";
params.configRequest = R.clone(params.config);
}
Api["modifyConfig"](params).then((res) => {
this.$notification.success({
class: "m-notice",
message: msg,
});
});
});
}
平台通用组件重构
- 通用组件的提取和封装
- 组件插件化开发和远程部署
- 排查通用组件和业务代码是否存在相互耦合,存在问题需要解耦
系统稳定性重构
- 排查常见错误
- Error:错误的基类,其他错误都继承自该类型
- EvalError:Eval 函数执行异常
- RangeError: 数组越界
- ReferenceError: 引用类型错误,引用不存在的变量时发生的错误
- SyntaxError:语法错误
- TypeError:类型错误,表示值的类型非预期类型时发生的错误
- URIError:以一种错误的方式使用全局 URI 处理函数而产生的错误
- 异常捕获加日志
内存泄漏排查
- 定时器
- 静态变量
- 意外的全局变量:排查 windows 上挂载的对象,在使用完需要释放
- 闭包引起的内存泄漏
- 没有清理的 DOM 元素引用
- 事件监听,没有解绑
无用资源删除
无用的资源,例如图片,css 样式,JS 等删除
性能部分重构:
- 排查并识别提高网站性能的优化点
- 排查和删除无用的界面层次,减少界面绘制时间
- 排查长代码页面,进行合理的模块化改造
- 公共样式代码抽取,css 预编译语言的合理使用,scoped 的合理使用