【开发糗事】 尴尬的 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,鬼知道我需要多久才能找到问题原因。

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