最近在写自己的第一个个人项目,开始写了很久了,但是每天没啥时间记录,趁着周末先记录一些思路和踩过的坑。
项目预计是做一个关于家装的电商网站,样式参考自土巴兔网站(我不是打广告!)。目前写了几个静态页面,写得有点审美疲劳,遂决定写一写后台JS换换心情~
之前听师兄说登录注册俩小时就能搞定,想一想不如就先写登陆注册吧,反正用不了多久。好吧,事实光速打脸了,我怎么就把自己对号入座了呢?(好吧,我菜了,真的要勤加练习啊!!!)每天抽空写一写,删删改改居然鼓捣了几天才写完(再一次泪奔)。
言归正传,还是说说我的实现思路和踩到的坑吧(T_T)。
声明:本项目目前采用jQuery框架!!!
注册界面我设计了四个输入框,分别是:手机号、用户名和两次密码。
手机号---满足一般手机号码格式:/^1[34578]\d{9}$/
用户名---4-16位,可选择包含字母,数字,下划线:/^[\w\u4e00-\u9fa5]{4,16}$/
密码---6-16位,其中必须含有数字、字母和特殊符号:/^.*(?=.{6,16})(?=.*\d)(?=.*[A-Za-z])(?=.*[!@#$%^&*?\(\)\.]).*$/
二次密码:比较和上述密码是否一致,返回判断结果。
坑1(严格来讲这不算坑,只是我学艺不精 >_<):密码的正则涉及一个先行断言。在符号“(?=” 和 “)” 之间加入一个表达式,它就是一个先行断言,用以说明圆括号内的表达式必须正确匹配。比如:/Java(?=\:)/ 只能匹配Java且后面有冒号的。
思路:每当输入框失去焦点(blur事件)时,默认用户完成当前输入框信息的录入,触发该输入框信息的正则验证,根据正则验证的结果决定是否给用户错误提示(HTML文档中input输入框的下一个兄弟元素next为错误提示内容,控制其display样式,正则验证失败进行提示display:block,成功则隐藏错误提示display:none)。
优化:此处我为了界面的美观性,当正则验证失败时,使输入框的边框变为红色(本来获得焦点时是主题色),这个也挺好办,就是麻烦了点——在CSS文件里写个样式,用JS动态为该input添加(addClass())/删除(removeClass())该样式就OK啦。
主要是验证手机号和用户名是否已被占用(不能重复注册嘛!),然后对用户进行错误提示。
用户名:未被占用--->不进行错误提示(意思就是这是可用的啦)
被占用---->进行错误提示
密码:功能需求同上,就是错误提示的内容不一样
思路:说在前面,此处的错误提示默认修改2.1中相应位置的错误提示内容。当前端页面的手机号/用户名输入框失去焦点时(此时默认用户输入完成),用jQuery封装好的ajax函数将用户当前输入的手机号/用户名发送给服务器端,在服务器端获取到前端传过来的数据,再将其传入数据库的查询sql语句中,根据返回结果result(result是一个数组,请牢记!)的长度,判断该手机号/用户名是否已被占用,并将此结果返回给前端页面,然后前端页面据此进行错误提示。
简单来讲,就是前端页面将用户当前输入的数据传给后端服务器进行数据库查询,并返回查询结果,前端页面再根据此结果决定是否进行错误提示。
前端验证和后台验证均通过且勾选同意条款的复选框(isAgree)
前端正则验证通过:regResult = true;
后台数据验证通过:resResult = true; ===> 注册成功
勾选同意条款的复选框:isAgree = true;
坑2:用户啥都不填(P.S.我要是用户,我绝对不会这么作!我发四!),直接点击注册。这时候各表单的blur事件就没有被触发,所以我只能在注册按钮的绑定事件里手动让各输入框失去焦点了(感觉这样会浪费资源,可是臣妾真的不造怎么解决了啊啊啊啊,要不然给用户提示个“请您善良”?会被打死吧)。
补充(2019.07.17):初始化上述三个值时,使其初始值均为false,不就对了嘛。就算用户什么都没填也没触发blur,初始的false没有被改变,这时候点注册也是通不过的呀。顶多浪费一个请求,去验证注册的成功与否,算下来开销还小一点。
好了,思路大致就如第二部分所讲,代码嘛,也不可能大段大段复制过来,这样可读性很差,捡重要的记录。
由于要进行四个正则验证(以后可能更多,哭),所以我果断决定先封装一个正则验证函数吧(我这么懒,怎么可能想写辣么多重复代码,逃...)
先贴上代码吧
function regStyle(t,str,reg){
if(t == null) return false;
else if(!(reg.test(str))){ //正则失败
$(t).addClass("error_border");
$(t).next().removeClass("hint_hide")
.addClass("error_hint");
return false;
}else{
$(t).removeClass("error_border");
$(t).next().addClass("hint_hide")
.removeClass("error_hint");
return true;
}
}
解释:要求传入的三个参数分别是:当前需要进行正则验证的输入框,当前需要进行验证的字符串(就是输入框内容啦,当然这个可以在封装的函数里获取到,可是我喜欢把需要分别获取到的数据写在一起,所以就写在各输入框blur事件的函数里啦),当前输入框内容需要满足的正则表达式。
解释:if-else条件语句:首先判断输入框内容是否为空,为空直接返回false(即正则验证结果),之后的代码比较简单就不详述了。需要说明的是$(t)语句是控制是否进行错误提示的,样式想法参见2.1中的优化部分啦。
各输入框验证的步骤大致一样,除了手机号和用户名需要进行后台验证之外,其余两项只需进行前端验证,此处以手机号验证为例(仅贴出前端验证的代码)。
// 1.验证手机号:满足一般手机号码格式
$("input[name='phone']").blur(function(){
// 1.1 获取页面相应数据,进行前端正则验证,并取得验证结果
phone = $(this).val();
var reg = /^1[34578]\d{9}$/;
regResult[0] = regStyle($(this),phone,reg);
});
解释:此处有一处需要注意,各输入框都需实时保存当前验证结果(确保用户反复进行修改之后保存的结果是正确的),所以我就干脆创建一个数组来分别保存各输入框的验证结果,只要输入框一失去焦点就实时更新正则结果。
解释:最后当用户点击注册按钮时(其实是个a标签),需要对regResult这个数组中的所有值进行判断,只要有一个为false(表明当前至少有一项输入框内容不符合正则表达式要求。)
话不多说,直接上代码,还是以手机号的后台验证为例。
先贴出前端页面发送的ajax请求:
// 1.验证手机号:满足一般手机号码格式
$("input[name='phone']").blur(function(){
// 1.2 向服务器发送请求,进行后台数据库验证——该手机号是否被占用
$.ajax({
url: "http://localhost:3000/register/isPhoneRepeat",
type: "get",
dataType: "json",
data: {phone},
async: false, /*注意注意 此处有个坑噢!*/
success: function(result){
// 1.2.1 将后台返回的查询结果放入数组中对应位置
resResult[0] = result.code;
// 1.2.2 查询返回1,表示该手机号未注册过,可以使用
if(result.code == 1){
$("input[name='phone']").next().children().next().html("手机号格式不正确!");
// 1.2.2 查询返回0,表明该手机号已注册过 ,被占用不能使用
}else if(result.code == 0){
$("input[name='phone']").next().children().next().html(result.msg);
$("input[name='phone']").addClass("error_border")
.next().removeClass("hint_hide")
.addClass("error_hint");
}
}
})
});
以下是服务器端注册路由中相应接口的代码:
// 判断手机号是否注册
router.get("/isPhoneRepeat",(req,res)=>{
console.log("接收到一个手机号的后台验证请求!");
var $phone = req.query.phone;
var sql = "SELECT * FROM user_info WHERE phone = ?";
pool.query(sql,[$phone],(err,result)=>{
if(err) throw err;
// console.log(result.length);
res.writeHead(200,{
// 定义内容类型:json数据格式,中文采用utf-8
"Content-Type": "application/json;charset=utf-8",
// 允许跨域请求
"Access-Control-Allow-Origin": "*"
});
// 如果找到该手机的数据则表示占用,若result为空则表示没有被占用,可以进行注册
if(result.length > 0){
res.write(JSON.stringify({code:0,msg:"该手机号已被占用!"}));
}else{
res.write(JSON.stringify({code:1,msg:"注册成功!"}));
}
res.end();
})
});
代码比较简单,主要理清楚前后端数据传送的关系即可(前部分思路中有讲哟)。
同上,先贴前端的代码,主要就是根据上两部分的验证和二次触发验证结果,决定是否满足注册条件,然后告知服务器,可以将这些数据插入数据库(也就是说,从现在开始,用户您嘞已经是我站的合法公民了)。
// 5.点击注册按钮向服务器发送请求,将用户信息数据存入数据库
$(".zh_reg a.signin_btn").click(function(){
// 5.1 是否勾选同意条款复选框
var isAgree = $("input[type='checkbox']").prop("checked");
// 5.2 点击注册按钮,使上面的表单(除复选框外)全部失去焦点,再次进行判断
// 可拦截用户什么都不填直接点击注册的行为
var inputArr = $("input.reg_info");
for(var input of inputArr){
$(input).blur();
console.log("在此使各input失去焦点了!");
}
// 5.3 获取前端正则验证结果
var reg = true,res = true;
for(var g of regResult){
if(!g){
reg = false;
break;
}
}
// 5.4 获取后台数据库验证结果
for(var s of resResult){
if(!s){
res = false;
break;
}
}
// 5.5 根据验证结果输出提示
if(!reg || !res){
// 坑2:alert函数是window自带函数,为同步CPU代码,会先于DOM渲染(异步)先执行
// 使用定时器延时函数将其变为异步函数,放入事件队列,就可以在DOM渲染完成之后才触发执行
setTimeout("alert('正确填写注册信息后才能注册喔~')",0);
}else if(!isAgree){
setTimeout("alert('如您同意我站的服务条款,请勾选同意喔~')",0);
}else{
$.ajax({
url: "http://localhost:3000/register/signin",
dataType: "json",
data: {phone,uname,upwd},
type: "post",
async: false,
success: function(result){
setTimeout(`alert('${result.msg}')`,0);
}
})
}
})
好吧,我承认我的代码写的很烂,可这也是我思考良久的智慧结晶!!!(我发现我写功能总是能想出很多奇葩的如果,如果用户不输入咋办,如果用户xxxx怎么办.....哇,真的要被自己转的牛角尖给折磨死)。
咳咳,说回正题(我怎么又跑题了),代码注释已经比较详细了,我就不再赘述(怎么感觉文字部分全是我自己的内心戏啊T_T)。
接下来是服务器端相应接口的代码:
// 将通过验证的新用户信息注册到数据库中并返回成功与否
router.post("/signin",(req,res)=>{
console.log("服务器接收到一个注册信息存储请求!");
var $phone = req.body.phone;
var $uname = req.body.uname;
var $upwd = req.body.upwd;
var sql = "INSERT INTO user_info VALUES(null,?,?,md5(?));"
pool.query(sql,[$phone,$uname,$upwd],(err,result)=>{
if(err) throw err;
console.log(result);
if(result.affectedRows > 0){
res.send(JSON.stringify({code:1,msg:"恭喜您,注册成功!"}));
}else{
res.send(JSON.stringify({code:0,msg:"啊哦,注册失败了"}));
}
})
})
代码差不多搬运到这,接下来讲一讲我踩过的坑及解决方法。
不要忘了前面还有两个标红了的坑!!!!!所以不要说我不识数,从3开始计数(身为程序媛,我是不是应该从坑0开始记录??)
坑3:原本想让后台返回的数据为-1和1来区分验证成功与否,这倒是没啥大问题,可是前端页面我用的是if(验证结果)进行判断,导致if里的该条件一直为true,百思不得其解,后来灵光一闪,才发现原来是自己蠢了orz...
解决:数字中只有0==false,其余的正数和负数均会自动转换为true。划重点:(-1==true) ===> true
坑4:点击注册按钮,触发其绑定事件(里面有alert弹框提示和使各输入框失去焦点触发二次前端+后台验证,后者涉及错误提示)。但alert()内容先于界面的错误提示,也就是说会出现alert弹框提示注册成功,但界面却有错误提示,需要第二次点击注册才会alert提示注册信息有误的诡异现象。
解释:alert函数是window自带函数,为同步CPU代码,会先于DOM渲染(异步)先执行。
解决:使用定时器延时函数setTimeout("",0)将其变为异步函数,放入事件队列,就可以在DOM渲染完成之后才触发执行。
坑4.1:不要忘了如果setTimeout()函数的第一个参数传入的不是函数而是语句的话(如本文中就是一段alert()语句),需要叫上引号,加引号,加引号!(重要的事情说三遍)。
此坑详见此文章(如有侵权,请速告知,立马删 0.0):https://www.cnblogs.com/zhenbianshu/p/8686681.html
坑5:多个ajax请求顺序执行问题:表单后台验证的ajax请求和注册信息的ajax请求执行顺序不能确定,导致重复点击注册时未能如预想中一样拦截重复注册请求。
解决:在每个ajax异步请求中加上async:false,使异步请求按代码出现先后顺序执行。(回去看看上面贴出的前端ajax函数,会有惊喜哦~~)
文章写到这里我也是很不容易了,我能说我写了几个小时吗!!!!!(我真的更加宁愿写代码 T_T),路漫漫其修远兮啊,功能完成到这里还是有很多不足的,先列出来有空好好改造改造,啊不对,是优化!
2019.9.12补充:新写了一篇较之这篇思路更清晰的注册文章,如有需要,传送门在此 ——>
同一页面多个注册入口的实现(JQ实现/接口已知)