摘要:而這一次的項(xiàng)目,原本以為開發(fā)挺順利的,但是開發(fā)完了,才發(fā)現(xiàn)自己犯了一個(gè)低級(jí)而嚴(yán)重的錯(cuò),這樣的一個(gè)失誤,我一直耿耿于懷。但是監(jiān)聽用戶退出頁(yè)面微信瀏覽器上面的那個(gè)返回或者關(guān)閉按鈕卻死活不行。也容易犯一些低級(jí)的錯(cuò)誤。
1.前言
前端從事了超過(guò)兩年,修復(fù)了無(wú)數(shù)的bug,寫了無(wú)數(shù)的bug;挖了很多次坑,填了很多次坑;犯了很多次錯(cuò),彌補(bǔ)了很多次,學(xué)習(xí)了很多次。一般而言,對(duì)于bug、坑,都是修復(fù)完了或者填完了,并且記住為什么會(huì)產(chǎn)生bug,為什么有坑,為什么犯錯(cuò),怎么解決的,下次怎么避免,就行了,就學(xué)習(xí)到了。而這一次的項(xiàng)目,原本以為開發(fā)挺順利的,但是開發(fā)完了,才發(fā)現(xiàn)自己犯了一個(gè)低級(jí)而嚴(yán)重的錯(cuò),這樣的一個(gè)失誤,我一直耿耿于懷。
2.起因在3月9號(hào)的這一天,公司有個(gè)活動(dòng),希望用答題活動(dòng)推廣自己的小程序。結(jié)果因?yàn)殚_發(fā)時(shí)間太緊,小程序在3月5號(hào)才提審。在3月8號(hào)早上,小程序還沒(méi)有審核,在不得已的情況下,只能把答題活動(dòng)以網(wǎng)頁(yè)的形式進(jìn)行,使用vue開發(fā)。由于在3月9號(hào)要用到這個(gè)答題活動(dòng),所以3月8號(hào)必須要完成開發(fā),測(cè)試,驗(yàn)收。
開發(fā)的過(guò)程,都挺順利,只是把小程序的一些代碼,改成vue開發(fā)移動(dòng)端網(wǎng)站的方式,把標(biāo)簽換了,樣式稍微重寫一下,項(xiàng)目就跑起來(lái)了,至于一些交互邏輯,由于不能使用小程序的API,只能另找良方代替,但問(wèn)題基本不大。
麻煩的一個(gè)需求就是:當(dāng)用戶沒(méi)答完題中途退出的時(shí)候,要記錄用戶的答題狀態(tài)。比如答了哪些題目,哪些題目錯(cuò)了,哪些題目正確了,拿了多少分等數(shù)據(jù)。在小程序里面,很輕松可以利用生命周期函數(shù) unload() 進(jìn)行監(jiān)聽。當(dāng)用戶沒(méi)答完題退出頁(yè)面的時(shí)候,把用戶當(dāng)前的答題數(shù)據(jù),傳給后臺(tái),讓后臺(tái)進(jìn)行保存。在用戶下次進(jìn)入頁(yè)面的時(shí)候,我可以根據(jù)后臺(tái)返回的用戶答題狀態(tài),進(jìn)行信息的展示。如果用戶沒(méi)答過(guò)題目,就重新開始答題,如果用戶上次退出的時(shí)候,沒(méi)答完題目,就按照退出時(shí)的進(jìn)度,讓用戶重新答題,如果答完了題目,直接顯示答題結(jié)果頁(yè)面。
這個(gè)需求不難實(shí)現(xiàn),小程序有 onload() 和 unload() 兩個(gè)生命周期函數(shù),只是在這兩個(gè)函數(shù)里面,調(diào)兩次接口而已。
但在網(wǎng)頁(yè)里面,監(jiān)聽用戶進(jìn)入頁(yè)面簡(jiǎn)單。但是監(jiān)聽用戶退出頁(yè)面(微信瀏覽器上面的那個(gè)‘返回’或者‘關(guān)閉’按鈕)卻死活不行。網(wǎng)上最多的解決方案是這個(gè),但是不知道是我使用方式有問(wèn)題還是人品問(wèn)題,壓根沒(méi)用,無(wú)論是微信開發(fā)者工具,還是安卓或者蘋果真機(jī)。
答案來(lái)自知乎:微信自帶瀏覽器環(huán)境內(nèi)左上角返回、關(guān)閉按鈕事件監(jiān)控?
pushHistory(); window.addEventListener("popstate", function(e) { alert("我監(jiān)聽到了瀏覽器的返回按鈕事件啦");//根據(jù)自己的需求實(shí)現(xiàn)自己的功能 }, false); function pushHistory() { var state = { title: "title", url: "#" }; window.history.pushState(state, "title", "#"); }
根據(jù)網(wǎng)上的方案,試了幾個(gè)(包括vue的生命周期函數(shù)),沒(méi)一個(gè)可行的。最后無(wú)奈之下,只能用一個(gè)蠢方法,用戶點(diǎn)擊每一題選項(xiàng)的時(shí)候,就把用戶當(dāng)前的記錄,通過(guò)接口發(fā)給后臺(tái),讓后臺(tái)記錄。這個(gè)就是我該文章說(shuō)的低級(jí)嚴(yán)重的失誤,想必大家也知道是怎么回事了。
3.失誤分析這次的答題活動(dòng),一共有三輪,每輪10道題,現(xiàn)場(chǎng)大概有500人答題。本來(lái)使用小程序開發(fā),不管用戶是沒(méi)答題就讓用戶可以開始答題,答題途中退出就記錄狀態(tài),答完題就顯示結(jié)果。在這個(gè)過(guò)程中,我跟后臺(tái)交互的只有兩次:一次是用戶進(jìn)來(lái)的時(shí)候獲取用戶答題進(jìn)度,一次是用戶答完了最后一題,發(fā)送用戶成績(jī),讓后臺(tái)記錄;或者中途退出,發(fā)送用戶答題進(jìn)度給后臺(tái),讓后臺(tái)記錄。
但是后來(lái)我在網(wǎng)頁(yè)中,由于暫時(shí)沒(méi)法監(jiān)聽用戶是否退出,所以選擇了用戶回答完每一題的時(shí)候,把數(shù)據(jù)發(fā)給后臺(tái),讓后臺(tái)答題進(jìn)度。這樣請(qǐng)求數(shù)就多了N倍。服務(wù)器的壓力就大了很多。
由于用戶進(jìn)來(lái),無(wú)論是小程序還是網(wǎng)站,都要請(qǐng)求接口,獲取用戶答題數(shù)據(jù),這次不在對(duì)比范圍。這樣原本小程序只需要和后臺(tái)進(jìn)行一次握手,但是在網(wǎng)頁(yè)中,采用了不合適的方式,和后臺(tái)握手次數(shù)變成了10次。足足多了9倍。如果是500人,每一輪從原本的500次,變成了5000次,三輪就從原本的1500次,變成了15000次!一般而言,10道題選擇題,是兩分鐘左右的回答時(shí)間,就相當(dāng)于在2分鐘內(nèi)服務(wù)器要響應(yīng)的次數(shù)多了9倍,這個(gè)擔(dān)子突然重了很多。而已這些請(qǐng)求,基本都有沒(méi)什么意義的,因?yàn)榻^大部分的人,10道題,大概兩分鐘的答題時(shí)間里面,不會(huì)中途退出,相當(dāng)于我做了一件沒(méi)意義,又消耗服務(wù)器性能的事情。
讓我耿耿于懷的原因,我一向?qū)φ?qǐng)求數(shù)嚴(yán)格的控制,雖然現(xiàn)在公司不怎么考慮性能,服務(wù)器壓力。但是這會(huì)引起我的強(qiáng)迫癥。
4.解壓方案由于答題活動(dòng),9號(hào)要使用,而我是8號(hào)晚上洗完澡的時(shí)候和同事聊天的時(shí)候才想起,所以我沒(méi)時(shí)間改了,因?yàn)楦牧艘彩切枰獣r(shí)間開發(fā),測(cè)試。9號(hào)由于同事請(qǐng)假,他的項(xiàng)目也由我負(fù)責(zé),也是比較趕的項(xiàng)目,我也沒(méi)那么多時(shí)間改。只能委屈一下服務(wù)器了。
說(shuō)是這樣說(shuō),但是關(guān)于其他的給服務(wù)器減輕負(fù)擔(dān)的方案,還是有比較講一下,算是給自己提個(gè)醒,也算是給大家提個(gè)醒。開發(fā)要注意一點(diǎn):不要急,不要急,不要急。
PS:當(dāng)時(shí)就是看著時(shí)間差不多是下午四點(diǎn)半了,然后還有兩個(gè)零散功能沒(méi)做,又要測(cè)試。找了很久的解決方案(監(jiān)聽微信的‘返回’或者‘關(guān)閉按鈕’)都沒(méi)下落的情況下,一下急了,腦袋放空,就想了那個(gè)方法。
cookie或者localstore記錄用戶的狀態(tài),這個(gè)應(yīng)該是最好的解決方案了,也應(yīng)該是最簡(jiǎn)單的解決方案。
比如使用cookie記錄用戶的答題進(jìn)度。在用戶每答一題的時(shí)候,就把cookie記錄到的數(shù)據(jù),更新一次。這樣只需要在用戶答完了最后一題的時(shí)候再把用戶的成績(jī)發(fā)給后臺(tái)就好,至于用戶中途退出也沒(méi)有,根據(jù)cookie判斷就好,如果cookie有記錄到用戶的數(shù)據(jù)。就顯示上次用戶退出時(shí)候的題目,讓用戶繼續(xù)答題。
原代碼:/** * @dedependson 點(diǎn)擊選項(xiàng) * @index 題目索引 number * @item 當(dāng)前選項(xiàng)對(duì)象 object */ chooseDo(index,item){ /*其他代碼略*/ let _this=this; let _data={ qid:_this.qid,//答題輪次,如"2"代表第二輪答題 questions:_this.questions,//已答題目,"1,2,3"這個(gè)表示id為1,2,3的題目已經(jīng)回答了 totalScore:_this.totalScore//當(dāng)前得分 } //發(fā)送請(qǐng)求,讓后臺(tái)記錄用戶答題進(jìn)度 this.$http.post(http_url.submit,_data,{emulateJSON:true}).then(res=>{ }) }
然后再到頁(yè)面加載的時(shí)候
mounted(){ this.$http.get(http_url.getQuestions,{ params:{ qid:this.qid } }).then(res=>{ res=res.body; //如果請(qǐng)求成功 if(res.code===0){ //如果用戶沒(méi)答完題 0-沒(méi)開始答題 1-沒(méi)答完題 2-答完題目 if(res.datas.status!==2){ //獲取答題的題目 this.questionList=res.datas.entryList; //如果題目小于10,就是開始答題了,但是沒(méi)答完(中途退出的原因) if(this.questionList.length<10){ //顯示答題頁(yè)面,讓用戶答題 this.questionListShow=true; } //否則就是沒(méi)答過(guò)題目,讓用戶答題 else{ //顯示開始答題頁(yè)面(答題首頁(yè),用戶需要點(diǎn)擊開始答題) } } //如果用戶已經(jīng)答完題,顯示結(jié)果頁(yè) else{ //代碼略 } } else{ alert(res.msg) } }) }cookie方案
chooseDo(index,item){ /*其他代碼略*/ let _this=this; let _data={ qid:_this.qid,//答題輪次,如"2"代表第二輪答題 questions:_this.questions,//已答題目,"1,2,3"這個(gè)表示id為1,2,3的題目已經(jīng)回答了 totalScore:_this.totalScore//當(dāng)前得分 } //保存cookie一天 //_this.qid作為答題輪次的標(biāo)識(shí) 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函數(shù)參考:ec-do
//設(shè)置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); },
然后再到頁(yè)面加載的時(shí)候,處理方式的改變。
mounted(){ this.$http.get(http_url.getQuestions,{ params:{ qid:this.qid } }).then(res=>{ res=res.body; //如果請(qǐng)求成功 if(res.code===0){ //如果用戶沒(méi)答完題 0-沒(méi)開始答題 1-沒(méi)答完題 2-答完題目 if(res.datas.status!==2){ //記錄答題輪次 this.qid=res.datas.qid; //獲取答題的題目 this.questionList=res.datas.entryList; //如果用戶中途退出,我們沒(méi)有和后臺(tái)對(duì)接口,后臺(tái)無(wú)法記錄用戶答題進(jìn)度,所以這次請(qǐng)求,返回的結(jié)果要么是沒(méi)開始答題,要么是答完題了。 //要還原用戶答題記錄,要使用cookie //如果存在cookie記錄,那么用戶肯定是至少答過(guò)一題,還原用戶答題進(jìn)度 let _answerQid=getCookie("answer-qid"+this.qid) _answerQuestions=getCookie("answer-qid"+this.qid).split(","); //字符串轉(zhuǎn)整數(shù) _answerQuestions.map(item=>+item); if(_answerQid&&_answerQuestions){ this.questionList.fifler(item=>{ //item.id是題目的id //如果題目的id存在,就過(guò)濾掉 _answerQuestions.indexOf(item.id)===-1 }); //顯示答題頁(yè)面,讓用戶答題 this.questionListShow=true; } //否則就是沒(méi)答過(guò)題目,讓用戶答題 else{ //顯示開始答題頁(yè)面(答題首頁(yè),用戶需要點(diǎn)擊開始答題) } } //如果用戶已經(jīng)答完題,顯示結(jié)果頁(yè) else{ //代碼略 } } else{ alert(res.msg) } }) }
代碼上面,可能用了 cookie 會(huì)復(fù)雜些,但是就多了幾行而已,差不了多少,反倒是減輕了很多請(qǐng)求。
在小程序沒(méi)有使用這個(gè)方案,就是考慮到用戶退出小程序,可能會(huì)清除緩存,雖然這個(gè)幾率不大,所以使用生命周期函數(shù)進(jìn)行unload()進(jìn)行監(jiān)聽,用戶退出就把用戶答題進(jìn)度提交給后臺(tái),讓后臺(tái)記錄,這樣的情況不會(huì)很多,甚至沒(méi)有,請(qǐng)求不會(huì)很多,所以當(dāng)時(shí)就用了這個(gè)方案。沒(méi)有使用cookie或者localstore。
注意幾點(diǎn):
1.無(wú)論什么情況,開發(fā)都需要一個(gè)清醒的頭腦,因?yàn)轭^腦不清醒,寫的都是bug,那個(gè)活動(dòng)是一個(gè)一次性的項(xiàng)目,如果是長(zhǎng)期的,我肯定會(huì)重構(gòu)的,因?yàn)楫?dāng)時(shí)寫的代碼太爛了。也容易犯一些低級(jí)的錯(cuò)誤。
2.不要為了小概率的事件想得太多,給自己,同事,服務(wù)器都帶來(lái)麻煩,也影響項(xiàng)目進(jìn)度。這次就是想得太多,結(jié)果提測(cè)的時(shí)間晚了,驗(yàn)收的時(shí)間晚了,自己也犯了錯(cuò)誤。想太多的后果可能就是撿了芝麻,漏了西瓜,甚至是偷雞不成蝕把米。
小結(jié)這次的的失誤就告一段落了,我也總結(jié)了一下,自己為什么會(huì)對(duì)這次失誤更更于懷。
1.最近一直在看怎么優(yōu)化代碼,讓代碼更有可讀性,可維護(hù)性。卻犯了請(qǐng)求數(shù)過(guò)多的錯(cuò)。顧此失彼啊。
2.第二個(gè)就是因?yàn)檫@次失誤,導(dǎo)致的后果太嚴(yán)重了,直接多了90%的請(qǐng)求。以往失誤導(dǎo)致的后果沒(méi)怎么嚴(yán)重。
3.以往犯錯(cuò)的時(shí)候,在項(xiàng)目上線之前能夠發(fā)現(xiàn),并且有時(shí)候改,這次不一樣,這次是發(fā)現(xiàn)了,但是沒(méi)時(shí)間改了。
4.那些以為不會(huì)有,不應(yīng)該犯的錯(cuò)??赡芫驮陬^腦不清醒的時(shí)候,就會(huì)犯這些錯(cuò)誤,無(wú)論什么時(shí)候都得留個(gè)神,這次也算是我自己提醒自己了。
不過(guò)結(jié)局是還算是好的,當(dāng)天因?yàn)闀r(shí)間關(guān)系,答題活動(dòng)沒(méi)有進(jìn)行,所以服務(wù)器沒(méi)有受到考驗(yàn)。如果當(dāng)天服務(wù)器承受不住壓力,崩了,我也可能要引咎辭職了!
好了,故事就是這樣了,有點(diǎn)日記的感覺(jué),希望大家諒解下。如果文章有什么地方寫錯(cuò)了,也歡迎指點(diǎn)交流。
--------------------華麗的分割線-------------------
想了解更多,關(guān)注關(guān)注我的微信公眾號(hào):守候書閣
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/93350.html
摘要:記一次頁(yè)面卡頓排查前述前段時(shí)間上線的一個(gè)移動(dòng)端的項(xiàng)目,由于開發(fā)時(shí)間倉(cāng)促,一直被用戶投訴頁(yè)面卡頓。這顯然要導(dǎo)致卡頓??偨Y(jié)這只是頁(yè)面卡頓的一個(gè)點(diǎn),當(dāng)然還有很多,我們的頁(yè)面卡頓就是由這樣一個(gè)一個(gè)的點(diǎn)造成的。 記一次頁(yè)面卡頓排查 前述 前段時(shí)間上線的一個(gè)移動(dòng)端的項(xiàng)目,由于開發(fā)時(shí)間倉(cāng)促,一直被用戶投訴頁(yè)面卡頓?,F(xiàn)在終于有時(shí)間來(lái)好好排查一下,看到底是什么原因。業(yè)務(wù)代碼都不是自己寫的,這是頗為頭疼的...
摘要:昨天看到一個(gè)大新聞拼多多在日凌晨出現(xiàn)漏洞,用戶可以領(lǐng)元無(wú)門檻優(yōu)惠券。拼多多本來(lái)就是家爭(zhēng)議頗大的公司,這次事件更是引發(fā)輿論熱議。有人估計(jì)全球?yàn)榇嘶ㄙM(fèi)的相關(guān)費(fèi)用有數(shù)億美元。軟件發(fā)布測(cè)試版讓用戶使用,就屬于一種黑盒測(cè)試。 昨天看到一個(gè)大新聞: 拼多多在20日凌晨出現(xiàn)漏洞,用戶可以領(lǐng)100元無(wú)門檻優(yōu)惠券 。一夜之間,被黑產(chǎn)、羊毛黨和聞?dòng)嵍鴣?lái)的吃瓜群眾薅了個(gè)底朝天,直到第二天上午9點(diǎn)才將優(yōu)惠券下...
摘要:但這并不意味著依賴版本是鎖死的。黃色表示不符合指定的語(yǔ)義化版本范圍,比如大版本升級(jí),升級(jí)可能會(huì)遇到兼容性問(wèn)題。文件可以列出不想打包的文件,避免把一些無(wú)關(guān)的文件發(fā)布到上。 作者: LeanCloud weakish 分享一些 npm 包管理工具的實(shí)用小竅門,希望能夠略微提高下前端、Node.js 開發(fā)者的生活質(zhì)量。 絕大多數(shù)前端和 Node.js 開發(fā)者每天的日常工作都離不開 npm,不...
摘要:當(dāng)奧巴馬贏得美國(guó)總統(tǒng)大選時(shí),頁(yè)面活躍度刷新了記錄。對(duì)于每一個(gè)成因,都應(yīng)制定相應(yīng)的預(yù)防措施,以減輕大規(guī)模事故。這種故障會(huì)通過(guò)許多層面進(jìn)入系統(tǒng)服務(wù)中,導(dǎo)致系統(tǒng)故障的發(fā)生。 作者介紹:Ben Maurer是Facebook的網(wǎng)絡(luò)基礎(chǔ)團(tuán)隊(duì)的技術(shù)領(lǐng)先者,主要負(fù)責(zé)整個(gè)Facebook面向用戶產(chǎn)品的性能和可靠性。Ben于2010年正式加入Facebook,基礎(chǔ)設(shè)施團(tuán)隊(duì)的成員。在加入Facebook之...
閱讀 3886·2021-10-13 09:39
閱讀 3886·2021-09-24 09:48
閱讀 1256·2021-09-01 10:30
閱讀 2581·2019-08-30 15:55
閱讀 1836·2019-08-29 16:39
閱讀 2351·2019-08-26 13:55
閱讀 3111·2019-08-26 12:23
閱讀 1690·2019-08-26 11:59