文章目录
  1. 1. 模块化加载与回调地狱
    1. 1.1. 样式&js的分离与模块化加载
    2. 1.2. 前端和后端公共库的封装
    3. 1.3. 回调函数和promise

这部分主要介绍了项目进行过程中遇到的一些问题及解决方案

模块化加载与回调地狱

样式&js的分离与模块化加载

这里实现的是js文件的按页面进行模块化加载,并不是我们常说的CMD/AMD等,关于他们,请参见我的另一篇文章js的模块化

  • 需求&问题

    这个项目中使用了Jade来实现视图分割,<head>标签、导航、<script>标签都是公用的。

    最开始页面比较少的时候,我都是直接将样式表/js代码等直接扔进上面的公用模板中进行渲染。但是到后面页面越来越多、页面行为越来越复杂的时候发现,每次加载页面都会附带加载一大堆当前页面不需要的样式表/js代码,造成了大量的http请求和性能的浪费。

  • 解决方案

    最后我采用了Jade的条件渲染结合Express中res.render()函数的第二参数实现了页面元素的模块化加载,加载哪些资源就在路由器中传入哪些资源,代码干净、可维护性高。

    代码如下:

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
//在layout.jade的<head>中,使用条件渲染
link(href='stylesheets/nav.css', rel='stylesheet')
link(href='stylesheets/common.css', rel='stylesheet')
-if (stylesheets)
each stylesheet in stylesheets
link(href='stylesheets/' + stylesheet, rel='stylesheet')
//在layout.jade的<script>中,使用条件渲染
-if (scripts)
each script in scripts
script(src='javascripts/' + script)
//在路由中,使用res.render(),实现模块化加载
//渲染主页时:
res.render('index', {
user: req.session.user,
stylesheets: ['jumbotron.css']
});
//渲染注册页时:
res.render('reg', {
scripts: ['reg.js'],
stylesheets: ['reg.css'],
success: req.flash('success').toString(),
error: req.flash('error').toString()
});

前端和后端公共库的封装

  • 需求&问题

    重复代码多,很多代码在未来的项目中,或者本项目中的其他部分都有可能发生重用。比如常用的正则表达式,验证唯一性,邮箱验证,密码确认等函数。

    工具函数和业务逻辑高度耦合,工具性函数业务逻辑混杂在一起,不利于代码维护。

  • 解决方案

    使用单例模式,对公用方法进行封装(较上一篇文章的utils多了登录验证工具函数)

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
var utils = {//前端库
exp: {
userNumReg: /^(AGUI-)(\d+)/,
userNameReg:new RegExp('[A-Za-z0-9_\-\u4e00-\u9fa5]+'),
userEmailReg: /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i,
userPwReg: /^[\S|\s]{6,16}$/,
userPhoneReg: /^(0|86|17951)?((13[0-9]|15[012356789]|17[05678]|18[0-9]|14[57])[0-9]{8})$/
},
common: {
overTime: function overTime(time, info, callback) {
var str = info;
var overTimer = window.setTimeout(function () {
callback(str+time, null);
}, time);
return overTimer;//返回定时器索引,供清除使用
}
},
reg: {//邮箱/两次密码/客官号正则/唯一性验证ajax
verifyInputEmail: function verifyInputEmail(em) {
return utils.exp.userEmailReg.test(em);
},
verifyInputPassword: function verifyInputPassword(pw, pw_re) {
return pw === pw_re;
},
verifyUserNum: function verifyUserNum(num) {
return utils.exp.userNumReg.test(num);
},
verifyUserName: function verifyUserName(name) {
return utils.exp.userNameReg.test(name);
},
verifyUserUnique: function verifyUserUnique(num, callback) {
var xhr = new XMLHttpRequest();
var flag =false;
xhr.open('GET', '/verifyUserUnique?num='+num, true);
var overTimer = utils.common.overTime(4000, '服务器超时', callback);
xhr.onreadystatechange = function () {
if (xhr.readyState==4 && xhr.status==200) {
window.clearTimeout(overTimer);
var res = window.JSON.parse(xhr.responseText);
console.log(res.instance);
if(res.code == '01') {
flag = true;//说明用户不存在,唯一
} else {
flag = false;//说明用户不存在,不唯一
}
typeof(callback) === 'function'?callback(null, flag):callback('typeError', null);
}
};
try {
xhr.send();
} catch (error) {
callback(error);
}
}
},
login: {
verifyUserLogin: function verifyUserLogin(num, password) {
}
}
}
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
var crypto = require('crypto');
var Question = require('../model/question');
var User = require('../model/user');
module.exports = {//后端库
parseToMd5: function parseToMd5(src) {
var md5 = crypto.createHash('md5');
var result = md5.update(src).digest('hex');
return result;
},
verifyUnique: function verifyUnique(collectionName, forCheck, callback, returnInstance) {
try {
// console.log(forCheck, callback, returnInstance);
collectionName.get(forCheck, function (err, instance) {
if (err) {
callback({error: err});
} else {
if (instance.length!=0) {
returnInstance?(callback({error:null, code:'02', instance:instance})):(callback({error:null, code:'02', instance:null}));
} else {
callback({error:null, code:'01', instance: null});
}
}
});
} catch (error) {
console.log(error);
}
},
checkLogin: function checkLogin(request, hasLoginCallback, notLoginCallback) {
typeof(hasLoginCallback) != 'function'?console.log('typeError') : null;
typeof(notLoginCallback) != 'function'?console.log('typeError') : null;
if (!request.session.user) {
notLoginCallback();
} else if (request.session.user) {
hasLoginCallback();
} else {
return 'checkLoginError';
}
}
}

回调函数和promise

  • 需求&问题

    回调函数的可读性好糟糕,在上一页的注册验证中,回调函数糟糕的可读性开始出现。

    比如AJAX验证用户唯一性时,大概是这样一个结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function ...(){
utils.reg.verifyUserUnique(num, callback) {
...
var overTimer = utils.common.overTime(4000, '服务器超时', callback);
xhr.onreadystatechange = function () {
...
callback();
}
};
try {
xhr.send();
} catch (error) {
callback(error);
}
}
}

如此多的callback和层级让我在回头自己阅读代码的时候都感觉头大,而且这还只是一个小业务的实现,等业务更多的时候,会不可避免的陷入回调地狱,如下(哈哈哈):

曾经有个小偷潜入某神秘机构,偷出机密代码的最后一页,打开一看

1
2
3
4
5
6
7
8
9
10
11
});
});
});
});
});
});
});
});
});
});
});

笑话转自aki@简书

文章目录
  1. 1. 模块化加载与回调地狱
    1. 1.1. 样式&js的分离与模块化加载
    2. 1.2. 前端和后端公共库的封装
    3. 1.3. 回调函数和promise