koa项目:云音乐应用5——注册页面前端渲染

在完成了基本的后端功能后,我们开始将前后端的内容连接起来。这一部分的内容会比较细碎。

注册页面功能

由于页面已经引入了jQuery,我们可以直接使用jQuery的语法。

补全头部按钮功能

image-20180820153735231

我们要实现的第一个小功能是登录和注册页面右上角的两个小按钮,点击他们跳转到各自页面。两行代码就可以搞定:

1
2
3
4
5
6
7
// 这是头部的部分代码
<!-- 登陆&注册栏 -->
<span>
<a class="register btn btn-normal btn-success" href="" id="register">注册</a>
<a class="login btn btn-normal btn-primary" href="" id="login">登录</a>
</span>
<!-- end 登陆&注册栏 -->
1
2
$("#register").attr("href", "/user/register");
$("#login").attr("href","/user/login");

注册页面检测用户名是否可用

老师的思路:

用户名输入框的值产生变化时,发送ajax请求检测用户名是否可用。

会产生的问题:

  1. 每输入一个字符都会发送一次请求,很影响用户体验
  2. 如果用户输入完成前,前半部分输入的值与注册过的用户名重名,会提示不可用
  3. 如果选中用户名进行复制或者其他操作,也会触发请求

解决方案:

  1. 减少提交请求的次数,设置定时器
  2. 把每次输入的值保存在一个临时变量中,每次发送请求前检测是否和保存的值相同,如果相同则不发送请求,以免重复请求
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
(function(){
function sendAjax() {
var timer;
var username;
return function(e) {
clearTimeout(timer);
if(username === $("#username").val()){
return; // 如果值没有变化,就不发送请求
}
timer = setTimeout(function(){
$.ajax({
url: "/user/check-username",
type: "post",
data: "username=" + $("#username").val(),
dataType: "json",
success: function(data){
username = $("#username").val();
alert(data.msg);
}
})
}, 500)
}
}

$("#username").on("keyup", sendAjax());
}){window}

我的思路:

  1. 用户焦点离开用户名再检查,即只在用户完成输入后检查
  2. 只返回用户名不可用时的提示信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(function(){
let username;
$("#username").on("blur", function(){
if(username === $("#username").val()){
return;
}
$.ajax({
url: "/user/check-username",
method: "post",
data: "username=" + $("#username").val(),
dataType: "json",
success: function(data) {
if
}
})
})
})(window)

最开始我直接用on("blur", fn)来实现判断用户名,这样的确是没问题的。

但是在尝试老师提到的选中文字复制会重复触发请求这一点上,同样有这一问题。

image-20180820204549790

所以我也加上了对用户名的值的判断,这样就不会重复提交了。

image-20180820204818591

显示密码强度

密码的强度测试三个维度

  • 是否包含字母
  • 是否包含数字
  • 是否包含特殊字符

如果我们假定一开始的安全值为0的话,每符合上述一条要求则加1,最终的数字为密码强度。我们为显示密码强度的地方留了三个空的div元素,对应密码强度,会有相应个数的div变成红色。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 先明确如何判断强度
function checkPass(str) {
let level = 0;
let reg1 = /[A-Za-z]/; //判断是否包含字母
let reg2 = /\d/; //判断是否包含数字
let reg3 = /\W/; //判断是否包含特殊字符
if(reg1.test(str)) level++;
if(reg2.test(str)) level++;
if(reg3.test(str)) level++;
return level;
}

// 再对密码进行动态判断(即每次密码值的变化都会进行一次判断)
$("#password").on("keyup", function(e){
let divs = $("#pwd-level").find("div");
for(let i = 0; i<divs.length; i++){
divs[i].style.backgroundColor = '';
}
let pass = e.target.value;
let level = checkPass(pass);
for(let i = 0; i < level; i ++) {
divs[i].style.backgroundColor = "red";
}
})

验证码功能

发送彩色验证码需要用到一个叫做captchapng2的中间件,它会随机生成一个4位的数字,然后会渲染成图片。

我们先在模板文件中将图片的src给补上

1
2
3
4
<li class="aw-register-verify">
<img class="pull-right" width="120" src="/user/captcha" >
<input type="text" class="form-control" name="vcode" placeholder="验证码" />
</li>

然后给用户路由加上对应的内容

1
2
3
4
5
6
7
8
9
10
11
router.get('/user/register', ctx => {
ctx.render('register');
})
.get('/user/login', ctx => {
ctx.render('login');
})
.post('/user/check-username', userController.checkUsername)
.post('/user/do-register', userController.doRegister)
.post('/user/do-login', userController.doLogin)
.post('/user/test-session',userController.test)
.get("/user/captcha",userController.getPic) //加载验证码

再根据中间件来写getPic函数

1
npm i captchapng2 -S
1
2
3
4
5
6
7
8
9
const captchapng = require("captchapng2");
exports.getPic = async ctx => {
let rand = parseInt(Math.random() * 9000 + 1000);
let png = new captchapng(80, 30, rand); // 宽、高、图片数字

ctx.session.vcode = rand;
ctx.response.set("Content-Type", "image/png");
ctx.body = png.getBuffer();
}

这时我们的注册页面上就有验证码了:

image-20180820220300970

但是还有一个问题,就是我们经常会遇见的验证码看不清需要点击换一个,所以我们还需要给验证码图片加上一个点击事件。

1
<img class="pull-right" width="120" src="/user/captcha" onclick="this.src='/user/captcha?' +Date.now()">

这样每次点击就会发送一个带时间戳的请求到user/captcha,从而请求到不同的图片了。

此外,我们还需要将验证码的验证加入到验证注册这一步骤中:

1
2
3
4
5
6
7
//user_controller.js
exports.doRegister = async ctx => {
let {username, password, email, vcode} = ctx.request.body;
if(vcode != ctx.session.vcode) { // 注意vcode是数字,ctx.session.vcode是字符串,需要隐性转换类型
return ctx.body = {code:"003", msg: "验证码不正确"};
}
}

邮箱验证

发现这里还有个小bug是邮箱没有验证。

我刚开始的做法是在模板文件里添加了一个正则验证邮箱的步骤,但是后来发现这样其实点击提示框之后还是可以继续完成注册,所以就学测试用户名是否可用那一部分一样,把验证邮箱加到了user_controller里,同时在验证注册的步骤里再验证一次邮箱。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// register.html模板文件的验证用户名部分添加验证邮箱的代码
(function(){
let email;
$("#email").on("blur", function() {
if(email === $("#email").val()){
return;
}
$.ajax({
url:"user/check-email",
method:"post",
data: "email="+$("#email").val(),
dataType: "json",
success: function(data){
email = $("#email").val();
if(data.code === "001") {
alert(data.msg);
}
}
})
})
})(window)
1
2
3
4
5
6
7
8
9
// user_router.js
router.get('/user/register', ctx => {
ctx.render('register');
})
.get('/user/login', ctx => {
ctx.render('login');
})
.post('/user/check-username', userController.checkUsername)
.post("/user/check-email",userController.checkEmail) //添加验证邮箱
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// user_controller.js
// 验证邮箱格式是否正确
exports.checkEmail = async ctx => {
let {email} = ctx.request.body;
let reg = /\w+@\w+.\w{2,3}/;
if(!reg.test(email)){
return ctx.body = {code: "001", msg: "请输入正确的邮箱"};
}
}

exports.doRegister = async ctx => {
let {username, password, email, vcode} = ctx.request.body;
// 验证邮箱格式,格式不正确无法完成注册
let reg = /\w+@\w+.\w{2,3}/;
if(!reg.test(email)){
return ctx.body = {code: "003", msg: "请输入正确的邮箱"};
}
}

这样注册页面需要的功能就全都补上了接下来处理登录页面。