背景
有了 React、Vue 等前端开发框架后,开发人员只用关注数据、而不用考虑 DOM 操作问题,前端开发相对容易多了。自从用了前端框架后,我已经六年没用过原生 JS 写过前端了。
最近维护一个老项目,纯 html 页面、数据控制全靠 jQuery 手堆。其中,有一个规则配置选项,十几个编辑页面都引用了它。旧的实现方式是拷贝,每个页面都拷贝了这段配置的代码,大约 500 行。真佩服当时的开发者,宁愿拷贝10次,也不抽取一个工具类。
上周要对这个功能调整,配置方式从前端分页变成后端分页。改十几个引用的地方,逐个拷贝确实挺费手的。心一横,重构一下吧。
卡住的几个点:
- 自定义 JS 类时,方法中如何访问类的变量?必须通过
this.变量名称
访问。 - 类的方法中通过 JQuery 绑定事件/各种回调函数中,想要访问这个类的属性,怎么办法?通过外层先定义一个 this 的拷贝对象,
copyThis = this
,引用该副本,或者事件回调用() => {}
表达式。 - 拼接的 html 中的事件方法是当前类的方法时,应该怎么访问?用引用该类的变量的方法调用。
- 弹框页面想要访问这个工具的方法,可以通过
parent.commonConfigUtil
访问。
公共代码抽取
重构思路,以 JS 类的思维,定义一个类,每个页面只需要创建这个类的对象,然后直接调用这个类的公共方法就可以,主要包含三类方法:
- 页面初始化时的按钮绑定事件
- 动态拼接数据到页面的配置显示区域
- 页面初始化时查询当前绑定的配置并回显
- 删除某配置
定义一个公共 js 文件,命名为 configUtil.js ,抽取的类定义如下:
javascript
var CommonConfigUtil = function (config) {
this.config = config;
this.att1 = 1;
this.att2 = 20;
this.pageData = 20;
this.baseUrl = config.baseUrl;
// 页面初始化绑定按钮事件
this.bindEvent = function bindEvent() {
$('#btn1').bind('click', event => {
const attr1 = this.attr1;
// TODO 使用外层类的属性完成按钮的绑定事件
});
$('#btn2').bind('click', event => {
const aa1 = this.attr2;
// TODO 使用外层类的属性完成按钮的绑定事件
});
};
// 动态拼接数据到页面,且绑定一个删除事件
this.addData = function addData(arr, now, count) {
let tr = '';
this.pageData.forEach(item => {
tr += '<div id="' + item.id + '">' +
'<div style="text-align: left;word-break:break-all">' +
'<h2 class="bkm" id="dd">' + item.name + '</h2><div style="float: right;"></div>' +
'<a href="javascript: void(0);" onclick="(event.stopPropagation()||(event.cancelBubble = true))&& commonConfigUtil.deleleData(\'' + item.id + '\')" style="float: right;"><i class="layui-icon"></i></a>' +
'</div>' +
'</div>';
});
$('#configList').append(tr);
});
// 页面初始化 ajax 回显数据
this.initConfig = function queryConfigById(id) {
const copyThis = this;
$.ajax({
type: 'GET',
url: this.baseUrl + "/xx.action",
data: {
"id": id
},
success: function (pageData) {
// TODO 数据使用,赋给外部类的属性
copyThis.pageData = pageData;
}
});
}
// 删除
this.deleteConfig = function deleteConfig(configId) {
});
}
公共引用
需要用的地方
javascript
<script src="${baseUrl}/xx/configUtil.js"></script>
<script>
var baseUrl = '${baseUrl}';
// TODO 从路径参数获取 ID
var id = '';
// 全局公告配置 变量
var commonConfigUtil = new CommonConfigUtil({
baseUrl: baseUrl,
});
//初始化页面
$(document).ready(function(){
commonConfigUtil.bindEvent();
// 编辑回显
if (id != '') {
commonConfigUtil.initConfig(id);
}
});
......
</script>
这样就能将 500 行的公告代码转换为十几行的工具类调用了。
启示录
这个抽取过程还是比较容易的,有几个卡壳的点,总结如下:
- 自定义 JS. 类的注意事项:方法中不能直接访问类的变量,必须通过
this.变量名称
访问。 - 类的方法中通过 JQuery 绑定事件、各种回调函数中,想要访问这个类的属性,可以通过
copyThis = this
,引用该副本,或者事件回调用() => {}
表达式,后者有一个问题,它会改变jQuery 的 $ 操作的 this 信息
。所以最简单的还是先定义副本对象,后面需要用类信息时都通过该副本对象。 - 拼接的 html 中的事件方法必须时当前页面定义的,如果是引用的某个类的,可以通过该类的变量来访问类对应的方法。拼接的元素
commonConfigUtil.deleleData()
这里真正触发时,会调用当前页面的全局变量commonConfigUtil
对象来访问删除方法。 - 页面可以
直接访问 commonConfigUtil
的各属性,没有权限问题,这点跟 Java 的类的属性的访问方式不同。 - 弹框页面想要访问这个工具的方法,可以通过
parent.commonConfigUtil
访问。
虽然代码拷贝很容易,但是大量重复拷贝,也很费劲呐,把所有的重复代码都删掉、换成这是十几行引用,搞了两天,手点鼠标都快断了。编码工作,懒是偷不了的:大脑想偷懒,徒手拷贝复制也挺受累。不是心累,就是手累!