1. Introduction

Worked on the front end for more than two years, fixed countless bugs and wrote countless bugs; Dug a lot of holes, filled a lot of holes; Made a lot of mistakes, made up for them a lot, learned a lot. Generally speaking, for bugs, pits, are repaired or filled in, and remember why there are bugs, why there are pits, why mistakes, how to solve, how to avoid the next time, on the line, you learned. This time, I thought the development was smooth, but after the development, I found that I had made a low-level and serious mistake. Such a mistake has been on my mind.

Cause 2.

On March 9th, the company had an activity, hoping to use the quiz activity to promote their mini-program. As a result, due to the tight development time, the mini program was only brought up on March 5. In the morning of March 8th, the mini program has not been reviewed yet. If we have to, we can only conduct the answering activities in the form of web pages and develop them using VUE. Since this activity will be used on March 9th, the development, test and acceptance must be completed on March 8th.

The development process, are pretty smooth, just put some small program code, to vUE development of mobile site way, change the label, the style rewrite a little, the project ran up, as for some interactive logic, because can not use the API of small program, can only find another good way to replace, but the problem is not big.

One troublesome requirement is to record the user’s answer status when the user drops out of the test. Like what questions you answered, what questions you got wrong, what questions you got right, how much grading data you got. In small applications, it is easy to use the lifecycle unload() function to listen. When the user did not finish the question exit the page, the user’s current answer data, to the background, let the background save. When the user enters the page next time, I can display the information according to the user answer state returned in the background. If the user did not answer the question, it will start again, if the user quit last time, did not complete the question, according to the exit schedule, let the user to answer again, if finished the question, directly display the answer result page.

This requirement is not difficult to implement. Applets have two life cycle functions, onload() and unload(), but within these two functions, the interface is called twice.

But on a web page, it’s easy to listen to the user enter the page. But monitoring the user’s exit page (the ‘back’ or ‘close’ button on wechat’s browser) doesn’t work. The most popular solution on the Internet is this one, but I don’t know whether I have a problem with the way I use it or my personality. It is useless, whether it is wechat developer tools, or Android or Apple real machine.

The answer comes from Zhihu: Wechat built-in browser environment in the upper left corner of the back button event monitoring?

pushHistory(); 
window.addEventListener("popstate".function(e) { 
    alert("I'm listening for the browser's back button event.");// Implement your own functionality according to your own requirements
}, false); 
function pushHistory() { 
    var state = { 
        title: "title".url: "#"
    }; 
    window.history.pushState(state, "title"."#"); 
}
Copy the code

I tried several (including vue’s lifecycle functions) based on the online solution, but none of them worked. Finally, they had to use a stupid method. When the user clicked on each option, they sent the user’s current record through the interface to the background, so that the background record. This is my article about the low-level serious mistake, I think you know what is going on.

3. Error analysis

There were three rounds of 10 questions in each round, and about 500 people answered the questions. The original use of small program development, no matter the user is no answer let the user can start to answer, answer the way out on the record state, answer the results will be displayed. In this process, I interact with the background only twice: one is when the user comes in to obtain the user answer progress, one is the user finished the last question, send user results, let the background record; Or quit halfway, send the user answer progress to the background, let the background record.

But later, I could not monitor whether users quit or not, so I chose to send the data to the background when users finished answering each question and let the background answer the progress. That’s N times more requests. The server is under a lot more pressure.

Because the user comes in, no matter be small program or website, all want to request interface, obtain user answer data, this time not compare scope. In this way, the small program only needs to shake hands with the background once, but in the web page, the inappropriate way, and the number of shaking hands with the background became 10 times. Nine times more. If you have 500 people, you go from 500 rounds to 5,000 rounds, and you go from 1,500 rounds to 15,000 rounds in three rounds! Generally speaking, 10 multiple choice questions, is about two minutes of answer time, which is equivalent to two minutes of server response times nine times, the burden is suddenly much heavier. But these requests, basically have no meaning, because the vast majority of people, 10 questions, about two minutes of answer time, will not quit, equivalent to ME to do a meaningless thing, and consumption of server performance.

The reason that annoys me is that I always have strict control over the number of requests, even though the company doesn’t think much about performance or server stress these days. But it causes my ocD.

4. Decompression scheme

Due to the activity of answering questions, I will use it on 9th, and I remembered it when I was chatting with my colleagues after taking a bath on 8th, so I have no time to change, because it also needs time to develop and test. Since my colleague asked for a leave on 9th, I was also in charge of his project, which was in a rush and I didn’t have much time to change it. Can only be wronged the server.

Say to say so, but about other to the server to reduce the burden of the program, or have a comparison to talk about, is to remind yourself, also is to remind everyone. One thing about development: Don’t rush, don’t rush, don’t rush.

PS: At that time, it was almost 4:30 in the afternoon, and then there were still two scattered functions to be tested. After looking for a solution for a long time (listening to wechat’s ‘back’ or ‘close button’), I was so anxious that MY head became empty and I thought of that method.

Cookies or localstore

Logging the user’s status is probably the best solution, and it should be the simplest solution.

For example, use cookies to record the progress of users. When the user answers a question, the data recorded by the cookie will be updated once. In this way, you only need to send the user’s score to the background when the user finishes the last question. As for the user’s exit, it is good to judge according to the cookie, if the cookie has recorded the user’s data. Show the last time the user quit the question, let the user continue to answer.

The original code:

/** * @dedependson Click option * @index title index number * @item Current option object */
chooseDo(index,item){
    /* Other code omitted */
    let _this=this;
    let _data={
        qid:_this.qid,// The number of rounds, such as '2' for the second round
        questions:_this.questions,// the answer to the question '1,2,3' has been answered
        totalScore:_this.totalScore// Current score
    }
    // Send the request, let the background record user answer progress
    this.$http.post(http_url.submit,_data,{emulateJSON:true}).then(res=>{
            
    })
}

Copy the code

Then it’s time for the page to load

mounted(){
    this.$http.get(http_url.getQuestions,{
        params:{
            qid:this.qid
        }
    }).then(res=>{
        res=res.body;
        // If the request succeeds
        if(res.code===0) {// If the user does not finish the question 0- did not start the question 1- did not finish the question 2- finished the question
            if(res.datas.status! = =2) {// Get the question to answer
                this.questionList=res.datas.entryList;
                // If the length of the question is less than 10, the answer is not finished.
                if(this.questionList.length<10) {// Display the answer page for the user to answer
                    this.questionListShow=true;
                }
                // If you do not answer the question, let the user answer the question
                else{
                    // Display the start answer page (answer home page, users need to click start answer)}}// If the user has already answered the question, the result page is displayed
            else{
                / / code slightly}}else{
            alert(res.msg)
        }
    })
}
       Copy the code

Cookie scheme

chooseDo(index,item){
    /* Other code omitted */
    let _this=this;
    let _data={
        qid:_this.qid,// The number of rounds, such as '2' for the second round
        questions:_this.questions,// the answer to the question '1,2,3' has been answered
        totalScore:_this.totalScore// Current score
    }
    // Save the cookie for a day
    //_this.qid indicates the number of rounds to be answered
    setCookie('answer-qid'+_this.qid,_this.qid,1);
    setCookie('answer-questions'+_this.qid,_this.questions,1);
    setCookie('answer-totalScore'+_this.qid,_this.totalScore,1);
}    
Copy the code

Cookie function reference: EC-do

/ / set the cookie
setCookie(name, value, iDay) {
    let oDate = new Date(a); oDate.setDate(oDate.getDate() + iDay);document.cookie = name + '=' + value + '; expires=' + oDate;
},
/ / get a 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 ' ';
},
/ / delete the cookie
removeCookie(name) {
    this.setCookie(name, 1.- 1);
}, 
Copy the code

Then, when the page loads, the processing changes.

mounted(){
    this.$http.get(http_url.getQuestions,{
        params:{
            qid:this.qid
        }
    }).then(res=>{
        res=res.body;
        // If the request succeeds
        if(res.code===0) {// If the user does not finish the question 0- did not start the question 1- did not finish the question 2- finished the question
            if(res.datas.status! = =2) {// Record the number of rounds
                this.qid=res.datas.qid; 
                // Get the question to answer
                this.questionList=res.datas.entryList; 
                // If the user drops out, we have no interface with the background, so the background cannot record the user's progress. Therefore, the result of this request is either not started or finished.
                // To restore the user's answer record, use cookies
                // If there is a cookie record, then the user must have answered at least one question
                let _answerQid=getCookie('answer-qid'+this.qid)
                _answerQuestions=getCookie('answer-qid'+this.qid).split(', ');
                // The string is converted to an integer
                _answerQuestions.map(item=>+item);
                
                if(_answerQid&&_answerQuestions){
                    this.questionList.fifler(item=>{
                        //item.id is the id of the subject
                        // If the topic id exists, filter it out
                        _answerQuestions.indexOf(item.id)===- 1
                    });
                    // Display the answer page for the user to answer
                    this.questionListShow=true;  
                }
                // If you do not answer the question, let the user answer the question
                else{
                    // Display the start answer page (answer home page, users need to click start answer)}}// If the user has already answered the question, the result page is displayed
            else{
                / / code slightly}}else{
            alert(res.msg)
        }
    })
}
Copy the code

The code might be a little more complicated with cookies, but it’s just a few more lines, not much more, and it saves a lot of requests.

In small program without using this scheme, which is considering the user exit applet, may clear the cache, although the risk is not big, so using life cycle function to unload to listen (), the user exit is the user answer schedule submitted to the background, let the background records, the situation will not many, not even, the request will not many, So that’s what was used. No cookies or localStore are used.

A few points to note:

1. In any case, development requires a clear head, because if you are not clear, you will write bugs. That activity was a one-off project, if it was a long-term project, I would definitely refactor, because the code written at that time was so bad. It’s also easy to make some stupid mistakes.

2. Do not think too much about small probability events, which will bring trouble to yourself, colleagues and the server and affect the project schedule. This time is to think too much, the results of the proposed test time was late, the time of acceptance was late, I also made mistakes. Think too much of the consequences may be to pick up the sesame, the leakage of watermelon, or even steal the chicken can not erode the rice.

2. Summary

This mistake ended, AND I also summarized why I would be more on this mistake.

1. Recently, I have been looking at how to optimize the code to make it more readable and maintainable. I made the mistake of counting too many requests. It’s one thing to lose.

2. The second is because of this mistake, the consequences are too serious, direct 90% more requests. The consequences of past mistakes were minor.

3. When I made mistakes in the past, I was able to find them before the project went online and sometimes corrected them. This time is different.

4. Mistakes you don’t think you should make. May be in the mind when not clear, will make these mistakes, no matter what time have to stay alert, this is also I remind myself.

However, the outcome is still good, because of the time, the answer activities did not take place, so the server was not tested. If the server collapses under the strain that day, I may have to resign, too!

Ok, that’s the story, a little diary feeling, I hope you understand. If there is something wrong in the article, you are also welcome to give advice and exchange.


——————– gorgeous dividing line ——————-

Want to know more, pay attention to my wechat public number: Waiting book pavilion