前端从事了超过两年,修复了无数的bug,写了无数的bug;挖了不少次坑,填了不少次坑;犯了不少次错,弥补了不少次,学习了不少次。通常而言,对于bug、坑,都是修复完了或者填完了,而且记住为何会产生bug,为何有坑,为何犯错,怎么解决的,下次怎么避免,就好了,就学习到了。而这一次的项目,本来觉得开发挺顺利的,可是开发完了,才发现本身犯了一个低级而严重的错,这样的一个失误,我一直耿耿于怀。javascript
在3月9号的这一天,公司有个活动,但愿用答题活动推广本身的小程序。结果由于开发时间太紧,小程序在3月5号才提审。在3月8号早上,小程序尚未审核,在不得已的状况下,只能把答题活动以网页的形式进行,使用vue开发。因为在3月9号要用到这个答题活动,因此3月8号必需要完成开发,测试,验收。前端
开发的过程,都挺顺利,只是把小程序的一些代码,改为vue开发移动端网站的方式,把标签换了,样式稍微重写一下,项目就跑起来了,至于一些交互逻辑,因为不能使用小程序的API,只能另找良方代替,但问题基本不大。vue
麻烦的一个需求就是:当用户没答完题中途退出的时候,要记录用户的答题状态。好比答了哪些题目,哪些题目错了,哪些题目正确了,拿了多少分等数据。在小程序里面,很轻松能够利用生命周期函数 unload()
进行监听。当用户没答完题退出页面的时候,把用户当前的答题数据,传给后台,让后台进行保存。在用户下次进入页面的时候,我能够根据后台返回的用户答题状态,进行信息的展现。若是用户没答过题目,就从新开始答题,若是用户上次退出的时候,没答完题目,就按照退出时的进度,让用户从新答题,若是答完了题目,直接显示答题结果页面。java
这个需求不难实现,小程序有 onload()
和 unload()
两个生命周期函数,只是在这两个函数里面,调两次接口而已。git
但在网页里面,监听用户进入页面简单。可是监听用户退出页面(微信浏览器上面的那个‘返回’或者‘关闭’按钮)却死活不行。网上最多的解决方案是这个,可是不知道是我使用方式有问题仍是人品问题,压根没用,不管是微信开发者工具,仍是安卓或者苹果然机。github
答案来自知乎:微信自带浏览器环境内左上角返回、关闭按钮事件监控?小程序
pushHistory();
window.addEventListener("popstate", function(e) {
alert("我监听到了浏览器的返回按钮事件啦");//根据本身的需求实现本身的功能
}, false);
function pushHistory() {
var state = {
title: "title",
url: "#"
};
window.history.pushState(state, "title", "#");
}
复制代码
根据网上的方案,试了几个(包括vue的生命周期函数),没一个可行的。最后无奈之下,只能用一个蠢方法,用户点击每一题选项的时候,就把用户当前的记录,经过接口发给后台,让后台记录。这个就是我该文章说的低级严重的失误,想必你们也知道是怎么回事了。浏览器
此次的答题活动,一共有三轮,每轮10道题,现场大概有500人答题。原本使用小程序开发,无论用户是没答题就让用户能够开始答题,答题途中退出就记录状态,答完题就显示结果。在这个过程当中,我跟后台交互的只有两次:一次是用户进来的时候获取用户答题进度,一次是用户答完了最后一题,发送用户成绩,让后台记录;或者中途退出,发送用户答题进度给后台,让后台记录。缓存
可是后来我在网页中,因为暂时无法监听用户是否退出,因此选择了用户回答完每一题的时候,把数据发给后台,让后台答题进度。这样请求数就多了N倍。服务器的压力就大了不少。服务器
因为用户进来,不管是小程序仍是网站,都要请求接口,获取用户答题数据,此次不在对比范围。这样本来小程序只须要和后台进行一次握手,可是在网页中,采用了不合适的方式,和后台握手次数变成了10次。足足多了9倍。若是是500人,每一轮从本来的500次,变成了5000次,三轮就从本来的1500次,变成了15000次!通常而言,10道题选择题,是两分钟左右的回答时间,就至关于在2分钟内服务器要响应的次数多了9倍,这个担子忽然重了不少。而已这些请求,基本都有没什么意义的,由于绝大部分的人,10道题,大概两分钟的答题时间里面,不会中途退出,至关于我作了一件没意义,又消耗服务器性能的事情。
让我耿耿于怀的缘由,我一贯对请求数严格的控制,虽然如今公司不怎么考虑性能,服务器压力。可是这会引发个人强迫症。
因为答题活动,9号要使用,而我是8号晚上洗完澡的时候和同事聊天的时候才想起,因此我没时间改了,由于改了也是须要时间开发,测试。9号因为同事请假,他的项目也由我负责,也是比较赶的项目,我也没那么多时间改。只能委屈一下服务器了。
说是这样说,可是关于其余的给服务器减轻负担的方案,仍是有比较讲一下,算是给本身提个醒,也算是给你们提个醒。开发要注意一点:不要急,不要急,不要急。
PS:当时就是看着时间差很少是下午四点半了,而后还有两个零散功能没作,又要测试。找了好久的解决方案(监听微信的‘返回’或者‘关闭按钮’)都没下落的状况下,一下急了,脑壳放空,就想了那个方法。
记录用户的状态,这个应该是最好的解决方案了,也应该是最简单的解决方案。
好比使用cookie记录用户的答题进度。在用户每答一题的时候,就把cookie记录到的数据,更新一次。这样只须要在用户答完了最后一题的时候再把用户的成绩发给后台就好,至于用户中途退出也没有,根据cookie判断就好,若是cookie有记录到用户的数据。就显示上次用户退出时候的题目,让用户继续答题。
/** * @dedependson 点击选项 * @index 题目索引 number * @item 当前选项对象 object */
chooseDo(index,item){
/*其余代码略*/
let _this=this;
let _data={
qid:_this.qid,//答题轮次,如'2'表明第二轮答题
questions:_this.questions,//已答题目,'1,2,3'这个表示id为1,2,3的题目已经回答了
totalScore:_this.totalScore//当前得分
}
//发送请求,让后台记录用户答题进度
this.$http.post(http_url.submit,_data,{emulateJSON:true}).then(res=>{
})
}
复制代码
而后再到页面加载的时候
mounted(){
this.$http.get(http_url.getQuestions,{
params:{
qid:this.qid
}
}).then(res=>{
res=res.body;
//若是请求成功
if(res.code===0){
//若是用户没答完题 0-没开始答题 1-没答完题 2-答完题目
if(res.datas.status!==2){
//获取答题的题目
this.questionList=res.datas.entryList;
//若是题目长度小于10,就是开始答题了,可是没答完(中途退出的缘由)
if(this.questionList.length<10){
//显示答题页面,让用户答题
this.questionListShow=true;
}
//不然就是没答过题目,让用户答题
else{
//显示开始答题页面(答题首页,用户须要点击开始答题)
}
}
//若是用户已经答完题,显示结果页
else{
//代码略
}
}
else{
alert(res.msg)
}
})
}
复制代码
chooseDo(index,item){
/*其余代码略*/
let _this=this;
let _data={
qid:_this.qid,//答题轮次,如'2'表明第二轮答题
questions:_this.questions,//已答题目,'1,2,3'这个表示id为1,2,3的题目已经回答了
totalScore:_this.totalScore//当前得分
}
//保存cookie一天
//_this.qid做为答题轮次的标识
setCookie('answer-qid'+_this.qid,_this.qid,1);
setCookie('answer-questions'+_this.qid,_this.questions,1);
setCookie('answer-totalScore'+_this.qid,_this.totalScore,1);
}
复制代码
cookie函数参考:ec-do
//设置cookie
setCookie(name, value, iDay) {
let oDate = new Date();
oDate.setDate(oDate.getDate() + iDay);
document.cookie = name + '=' + value + ';expires=' + oDate;
},
//获取cookie
getCookie(name) {
let arr = document.cookie.split('; '),arr2;
for (let i = 0; i < arr.length; i++) {
arr2 = arr[i].split('=');
if (arr2[0] == name) {
return arr2[1];
}
}
return '';
},
//删除cookie
removeCookie(name) {
this.setCookie(name, 1, -1);
},
复制代码
而后再到页面加载的时候,处理方式的改变。
mounted(){
this.$http.get(http_url.getQuestions,{
params:{
qid:this.qid
}
}).then(res=>{
res=res.body;
//若是请求成功
if(res.code===0){
//若是用户没答完题 0-没开始答题 1-没答完题 2-答完题目
if(res.datas.status!==2){
//记录答题轮次
this.qid=res.datas.qid;
//获取答题的题目
this.questionList=res.datas.entryList;
//若是用户中途退出,咱们没有和后台对接口,后台没法记录用户答题进度,因此此次请求,返回的结果要么是没开始答题,要么是答完题了。
//要还原用户答题记录,要使用cookie
//若是存在cookie记录,那么用户确定是至少答过一题,还原用户答题进度
let _answerQid=getCookie('answer-qid'+this.qid)
_answerQuestions=getCookie('answer-qid'+this.qid).split(',');
//字符串转整数
_answerQuestions.map(item=>+item);
if(_answerQid&&_answerQuestions){
this.questionList.fifler(item=>{
//item.id是题目的id
//若是题目的id存在,就过滤掉
_answerQuestions.indexOf(item.id)===-1
});
//显示答题页面,让用户答题
this.questionListShow=true;
}
//不然就是没答过题目,让用户答题
else{
//显示开始答题页面(答题首页,用户须要点击开始答题)
}
}
//若是用户已经答完题,显示结果页
else{
//代码略
}
}
else{
alert(res.msg)
}
})
}
复制代码
代码上面,可能用了 cookie 会复杂些,可是就多了几行而已,差不了多少,反却是减轻了不少请求。
在小程序没有使用这个方案,就是考虑到用户退出小程序,可能会清除缓存,虽然这个概率不大,因此使用生命周期函数进行
unload()
进行监听,用户退出就把用户答题进度提交给后台,让后台记录,这样的状况不会不少,甚至没有,请求不会不少,因此当时就用了这个方案。没有使用cookie或者localstore。
注意几点:
1.不管什么状况,开发都须要一个清醒的头脑,由于头脑不清醒,写的都是bug,那个活动是一个一次性的项目,若是是长期的,我确定会重构的,由于当时写的代码太烂了。也容易犯一些低级的错误。
2.不要为了小几率的事件想得太多,给本身,同事,服务器都带来麻烦,也影响项目进度。此次就是想得太多,结果提测的时间晚了,验收的时间晚了,本身也犯了错误。想太多的后果可能就是捡了芝麻,漏了西瓜,甚至是偷鸡不成蚀把米。
此次的的失误就告一段落了,我也总结了一下,本身为何会对此次失误更更于怀。
1.最近一直在看怎么优化代码,让代码更有可读性,可维护性。却犯了请求数过多的错。顾此失彼啊。
2.第二个就是由于此次失误,致使的后果太严重了,直接多了90%的请求。以往失误致使的后果没怎么严重。
3.以往犯错的时候,在项目上线以前可以发现,而且有时候改,此次不同,此次是发现了,可是没时间改了。
4.那些觉得不会有,不该该犯的错。可能就在头脑不清醒的时候,就会犯这些错误,不管何时都得留个神,此次也算是我本身提醒本身了。
不过结局是还算是好的,当天由于时间关系,答题活动没有进行,因此服务器没有受到考验。若是当天服务器承受不住压力,崩了,我也可能要引咎辞职了!
好了,故事就是这样了,有点日记的感受,但愿你们谅解下。若是文章有什么地方写错了,也欢迎指点交流。
--------------------华丽的分割线-------------------
想了解更多,关注关注个人微信公众号:守候书阁