欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

与IE兼容性纠缠的这几个月(续) jqueryieperformance 

程序员文章站 2022-03-17 16:18:45
...
    前段时间写了篇博客,简单描述了之前参与的系统中解决IE兼容性问题的一点过程。对于我个人而言,是非常厌恶微软IE789浏览器的,经常和同事开玩笑说,“IE789简直就是阻止人类进步的绊脚石”。我们这个系统从今年3月份正式上线,一直到现在,还在处于天天救火严重bug的状态,这其中包含了所有你能想出来的原因,当然之一就是IE兼容性问题。

    当然,不可否认,在解决IE兼容性问题的过程中,确实变相找到一些乐趣。至少比重复写着controller,service,dao,onclick,$().val()来的有意思。加上,这周中,用我上一篇文章中提到的一点,帮助了同事解决了迫在眉睫的问题,觉得还是有意义的。

    今天,要得瑟的内容,层面上,要比上一篇更高一个层次,就是性能问题。性能问题,可以说和ie无关,也可以说有关。比如你无端跑了极大量的循环,无论哪个浏览器里,都是不应该的。当然,在chrome里,可能是一瞬间,在ie里,却需要N秒。所以,又是和IE有关。

   
    以下是三个Case:

    1. 对HandsonTable单元格赋值

    handsonTable是一个国外的开源工具,用来提供仿excel的js表格控件,包括合并单元格等特性,是非常优秀的工具。

    我们的系统,打开某个页面的时候,非常慢,在chrome下也需要2秒+的样子,在IE9下需要10秒,IE8下需要14秒+,基本就是不可忍了。于是,我尝试着去解决这个性能问题。首先,当然是使用工具检测一下,在哪些代码上可能存在瓶颈。我选择的是chrome的profiles工具。有些人可能会觉得在chrome下测比较难,因为chrome跑js性能太好。但是,问题代码和正常代码的耗时比例,其实差别不会太大。我使用IE9的profiles测试过,心里是有底的。而且我在mac的虚拟机里跑一个ie9,加上win7在高分屏下的体验,总之就是不愿意。。

    通过profiles对js方法运行时间的统计,通过tree结构的结果列表分析,最终定位于handsontable的一个对单元格赋值的API。

    这里要插一句,定位到问题所在的过程,并非一帆风顺。不像自己写的代码,出了异常,能直接定位到某一行的某个变量。前端js里大多数都是第三方库,自己写的出问题的几率很少,很大程度上,是自己没用好第三方库的api,导致问题爆发在第三方库的逻辑中,而这些逻辑往往比较抽象。总之,依靠缩小范围,推理,猜测,动用一切经验和智慧,找出关键点。

   handsontable的这个api,setDataAtCell,很明显是给一个单元格赋值,再看我们使用的逻辑,是个使用了两层for循环给一个表格赋上初始值。当时就明白了,很明显,开发的过程中没想到实际使用时会出现上百列的场景,完全没想到这么多列下,一个一个赋值会慢的这么明显。

   解决的办法,我第一反应就是,批量赋值。尽管当时我完全不了解handsontable的api,但是我心里想,肯定有批量赋值的api,不然它就是shit!而且批量赋值的操作在底层一定不会像我们这样循环去做,不然还是shit!打开官网,粗粗扫了一遍api文档,操,没找到…… 然后开始细细地看,忽然发现setDataAtCell方法居然还可以传数组!由于官网文档写的实在太简陋,足足体会了好一会,才认定,一定就是它了。

   于是,我把双层for循环改成单层for循环。其实就是,一行一行赋值,而不是一个一个单元格赋值。耗时瞬减!IE下足足快了5秒。

   解决完这个问题,感觉还是很良好的,而且解决思路简单,没有副作用。但是下面这个case,就有点麻烦了。

   2. jQuery UI的sortable组件

    紧跟着上一个case,从原来10s+减少到5s+,已经提升了一倍性能,但是还不够,没有人愿意等5秒钟才打开页面。

    我本想说,用ie89的人活该,但是想想他们也是无辜的,因为有些老系统只支持老浏览器,总不能让用户一天到晚切换浏览器吧。这就是个恶心循环!

    接着说,依然使用profiles检测,可以看见前面setData部分的代码运行耗时比例已经降低很多,原来可是达到了70%+。此时另一个堆栈挑起了性能慢的大梁。通过tree结构分析,似乎涉及到jQuery UI的代码,但那时不了解代码逻辑,也就不知道为什么慢在jQuery UI上了。主要是我不知道 sortable和dragglable是属于 jQuery UI的,囧。。

    忽然想到,console.time()方法,可以测量代码执行时间,于是我在出问题的方法里,切了好几段,分别统计执行时间。运行,很明显,在创建sortable对像的方法上,消耗了大量的时间!问题点找到了,开始读逻辑,才发现原来给上文表格中的每一个单元格,都绑定了一个sortable组件。因为交互逻辑是,可以拖拽一些东西到表格内。

    google了一下,全世界都在抱怨sortable组件创建时的消耗大。当时一想,坏了,要是设计就必须放这个组件,就不能随意砍掉,而且几乎没有优化余地啊。

    此时,走了一个弯路,由于是失败的弯路,准备放在最后讲。

    读通原有设计思路是不可避免得了,耐下心读吧。同时还去官网学习了我不熟悉的sortable,dragglable,droppable组件的使用。然后,惊!天!大!发!现!

    先科普一下,sortable的作用是,某个区域内的元素,可拖动交换位置,比如一个列表中的每一行。draggable定义了可拖动元素,而droppable是指该区域可被放置拖动的元素。因此sortable就好比,draggable和droppable的结合。
   

    页面逻辑是,用户从其他地方拖动一个元素到表格的一个单元格内,再拖动一个新元素进来时,会覆盖。然后顿时震惊了,现在将单元格本身作为一个sortable区域,而内部却永远只需要存在一个元素,就完全没有必要成为sortable区域啊,又不需要排序!于是将sortable组件改为droppable组件的想法自然产生。

    由于我也不知道droppable的性能会被sortable好多少,所以怀着惴惴不安的心情做了尝试,中间省略很长时间调节代码逻辑的过程,这也不是容易的过程。。。

    确实快了很多!之前IE9下降到5秒+的过程,如今只需要2秒了!换句话说,两个优化以后,ie9从10秒降到2秒,同时,ie8也从14秒降到4秒。

    至此,这部分就不准备再调节了,虽然理论上还存在优化的余地。

   3. jquery选择器 :not

    这个Case准备简单掠过,因为直接砍掉了一部分逻辑,不算真正改好,(其实我觉得原逻辑也不好,所以砍掉就砍掉了。。。。嘿嘿偷偷的)

   通过profiles和 console.time 定位到了一个jquery选择器居然执行了好几秒,这里含有多个选择器的并列,还涉及到not语法,通过调试,发现单个使用not时没有性能问题,但与其他并列选择器的同时使用,就出奇的慢。关于这个我就没有深究,心想这not这种东西就和sql里not,like之流有异曲同工之妙,总有慢的理由。。。。遂,砍之。。

  
   到这里,经历过的三次调优就结束了,之后由于参与其他任务,没时间调优,所以就放置一边了。总之,和前一篇博客一样,记一件有意义的事情。


——————————————————————————————————

前面讲过一个弯路,虽是弯路,但是挺有意思的尝试。

由于sortable慢,一开始又不知道可以怎么优化。于是想着能不能将这种卡顿感弱化。js是一个单线程的语言,放在浏览器里就是,如果一直在忙于执行一段任务,其他事情就会被卡住,包括用户页面操作,页面无响应,弹出IE是否结束脚本警告框,等等。

那么如果我自己模拟一个时分复用,将5秒钟的任务分割成N分,每5/N秒中断一次,因为有了中断,js线程就不会永远卡在当前任务上,有机会让其他任务插一脚执行,比如页面点个按钮啥的。在*上拷了段js代码,用promise辅以setTimeout,模拟出一个时分复用的样子。

当然结果还是不理想啦,感觉卡顿感更严重了啊,页面点击时好时坏的,想着不能从根本上解决,还是不行啊,于是只能开始啃上下文代码。。。
——————————————————————————————————