【開發糗事】 尷尬的 Click 重複執行問題

開發糗事 – 尷尬的 Click

大清早剛上班就遇到了一個極蠢的 BUG,此事必須記錄以作提醒。
我的問題現象很可能很多人都遇到過,但我的問題原因你絕對猜不到。

問題發現

一大早,系統負責人跑過來找我:點擊圖片後彈窗顯示信息的功能怎麼彈出了兩個窗口?也要關兩次纔會關掉?

我:好,我改一下。馬上就好。(心想:嗯?難道 click 事件寫重複了,或者彈窗方法寫重複了?)

開始調試

  • 查看 click 事件綁定的相關代碼

    因爲這個功能是全局的,所以寫在 common.js 中,並且,由於節點是動態生成的,所以使用 on 事件監聽 click 事件。

    /**
     * 點擊圖片彈窗顯示詳情信息
     */
    $(".has_small_img").on("click", ".small_img", function() {
      var src = $(this).attr("src");
      layer.open({
        type: 2,
        title: "",
        closeBtn: 1,
        shadeClose: true,
        maxmin: true,
        area: ["95%", "95%"],
        skin: "yourclass layer-ext-myskin",
        content: $.wrapUrl("/Base/ShowMessage?src=" + src),
        success: function(layero, index) {}
      });
    });
    

    layer 只 open 了一次。

    全局搜索了一下 has_small_img 和 small_img 。沒有不合理的地方。
    (有點懵…)
    那會是哪裏重複調用了呢?

  • 啓動項目,問題重現

    既然 click 事件綁定的部分沒有發現錯誤,那隻好先將項目運行起來,重現一下問題,看一看是否還涉及到別的操作?

    前方高能!!!

    1. 點擊圖片。走你~
    2. 唰…唰…兩個彈窗(咦?還真是兩個…en…換個圖片試試)
    3. 換個圖片。點…
    4. 唰…唰…唰(0.0 嗯?什麼鬼?三個?還多一個?)
    5. 再換一個圖片。click…
    6. 唰…唰…唰…唰(臥槽,點一次多一個???偷偷擦汗。。。幸虧負責人小姐姐只點了一次,趕緊偷偷改…)

    測到這裏,基本可以確定兩個字 嵌套

    但嵌套也分很多種,到底是哪裏嵌套了呢?


    對於嵌套綁定,最常用的方法 off 和 unbind 先試一遍(儘管我認爲我的代碼中並沒有嵌套綁定,但現在運行的效果確實是嵌套的效果)。

    $(".has_small_img")
      .off("click", ".small_img")
      .on("click", ".small_img", function() {});
    
    $(".has_small_img")
      .on("click", ".small_img", function() {})
      .off("click", ".small_img");
    
      ...
    

    試了各種解綁方式,結果發現,並沒有什麼卵用。

  • 版本還原,追查原因

    此時,突然想起一個非常重要的線索,該功能在剛做好提交之後測試時正常的(絕對是正常的,不然我不會提交)。於是,git 的優勢就凸顯出來了。

    通過 git,我將代碼還原到最後一次修改該功能的版本,測試,正常。

    (總算有點小安慰,至少可以確認該功能的代碼是沒有問題的,不至於像無頭蒼蠅一樣亂撞)


    看來,應該是受到其它功能的影響。

    繼續追查 git,發現該問題發生在 516 版本(添加了首頁 Gantt 圖加載的功能)之後,而這個版本僅僅新增了這一個功能,雖然這個 Gantt 圖加載的功能看似與 click 事件無絲毫關聯。

    將代碼還原爲最新版本,屏蔽了 Ganntt 圖加載的方法,測試,正常。(可以確定,問題就發生在這個看似毫無關係的功能上)。

  • 問題確認,大寫的尷尬。

    進入 Gantt 圖所在頁面,進一步排查原因。

    額,瞬間無語,罪魁禍首居然是:

    <script src="~/Scripts/common.js" />
    

    一個頁面,引入公用的 js,看似再正常不過的一件事,怎麼會是這個問題的罪魁禍首呢?

  • 問題原因

    沒錯,理論上,引入公用 js 確實是個正常操作。

    但是,這是一個完整的項目,公用 js 早已在佈局頁面中引入過一次,這裏可不就是重複引入了嗎?

    而這個頁面又是首頁的局部視圖(PartialView),在首頁加載的時候也進行了加載

    每次更換數據時,圖片和其它數據都是異步刷新的,圖片更新了,當然 Gantt 圖頁面也重新加載了,而 Ganntt 圖每重新加載一次,就多綁定一次 click 事件。(因爲之前有說過,這個 click 事件是全局監控的,所以在 common.js 加載時就會運行)

  • 自己的坑、自己填

    尷尬的問題來了,罪魁禍首的一行代碼,恰好是我自己故意加上去的…


    我明明知道佈局頁面已經加載過,爲什麼還會加這個呢?

    剛纔也有提到,Gantt 頁面是一個 PartialView,顧名思義,就是鑲嵌在主頁面的一個局部視圖,它在運行的時候,使用的是主頁面引入的 js 文件,當然也包括 jquery、common.js 等。

    但是,當時在做這個局部視圖的時候,恰好主頁面還沒有完成,無法嵌套進去,所以我是單獨對這個局部視圖測試的,沒有了主頁面的支持,jquery、layer、common.js 等都無法使用,所以,就臨時對這個局部視圖引用了這些 js 文件來做臨時測試,結果提交的時候將這件事忘得一乾二淨。
    這要只是引入了一些插件方法之類的,雖然浪費了資源,但好歹不會出現功能性問題,可巧的就是,恰好引進去了一個包含全局監聽事件的 js 。

總結

  • 測試代碼及時刪除

    即使不能及時刪除,也一定要做好備註,標明時間、原因、以及測試字樣,養成良好的編碼習慣;

  • 多用、巧用版本控制軟件

    本次問題排查過程中,Git 功不可沒。它幫助我在最短的時間內定位到了問題所在。
    一個問題的罪魁禍首,出在另一個幾乎完全不相干的位置,恰好又是一個最容易被遺忘、被忽略的地方。
    如果沒有 Git,鬼知道我需要多久才能找到問題原因。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章