在完成了基本的后端功能后,我们开始将前后端的内容连接起来。这一部分的内容会比较细碎。
注册页面功能
由于页面已经引入了jQuery,我们可以直接使用jQuery的语法。
补全头部按钮功能
我们要实现的第一个小功能是登录和注册页面右上角的两个小按钮,点击他们跳转到各自页面。两行代码就可以搞定:
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>
|
1 2
| $("#register").attr("href", "/user/register"); $("#login").attr("href","/user/login");
|
注册页面检测用户名是否可用
老师的思路:
用户名输入框的值产生变化时,发送ajax请求检测用户名是否可用。
会产生的问题:
- 每输入一个字符都会发送一次请求,很影响用户体验
- 如果用户输入完成前,前半部分输入的值与注册过的用户名重名,会提示不可用
- 如果选中用户名进行复制或者其他操作,也会触发请求
解决方案:
- 减少提交请求的次数,设置定时器
- 把每次输入的值保存在一个临时变量中,每次发送请求前检测是否和保存的值相同,如果相同则不发送请求,以免重复请求
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 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)
来实现判断用户名,这样的确是没问题的。
但是在尝试老师提到的选中文字复制会重复触发请求这一点上,同样有这一问题。
所以我也加上了对用户名的值的判断,这样就不会重复提交了。
显示密码强度
密码的强度测试三个维度
如果我们假定一开始的安全值为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 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(); }
|
这时我们的注册页面上就有验证码了:
但是还有一个问题,就是我们经常会遇见的验证码看不清需要点击换一个,所以我们还需要给验证码图片加上一个点击事件。
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
| exports.doRegister = async ctx => { let {username, password, email, vcode} = ctx.request.body; if(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
| (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
| 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
|
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: "请输入正确的邮箱"}; } }
|
这样注册页面需要的功能就全都补上了接下来处理登录页面。