Skip to content

用ControlJS优化JavaScript加载速度

这是来自TAOBAO UED博客中的一篇文章。

长时间以来阿里妈妈的广告载入策略一直存在些问题,很多页面也因为阻塞式的广告载入而被拉低性能,影响用户体验。毕竟阿里妈妈广告的渲染依赖于诸多嵌套的document.write。ControlJS的目标就是解决js的阻塞式渲染,因此灵玉急不可耐想与同仁们共同去揭秘ControlJS……

Steve Souders在2010年12月份发布了ControlJS项目,该项目是让开发者更好的去控制javascript文件的下载和执行,从而提高了页面脚本的加载速度。

Steve提出了一个非常具有创造性的思想,就是预先异步下载javascript文件而不解析执行,直到需要的javascript处理时才去真正的执行。这一点得到了很多人的关注与验证。Nicholas Zaka也因ControlJS引发了很多思考,并分析了ControlJSLABjs 的区别所在,详细内容可以阅读Thoughts on script loadersSeparating JavaScript download and execution。Steve使用3篇博闻详细介绍了ControlJS异步加载延迟执行重写document.write

  • ControlJS的原理

异步加载

ControlJS本身是异步进行加载的,首先将script的标签type属性值更改为浏览器无法识别的类型,这样浏览器不会认为这是一个脚本。本身异步加载的ControlJS执行时开始遍历type=”text/cjs”的script标签(包括内嵌脚本),如果存在”DATA-CJSSRC”属性将创建IMAGE或者OBJECT对象(依赖浏览器而选择),去异步预下载脚本文件并缓存文件,直到window.onload时解析并执行javascript,同时第二次去遍历遗漏的script标签。具体操作可看Async WITH ControlJS

延迟执行

浏览器在解析执行javascript阶段是阻塞任何操作的,这时的浏览器处于假死状态,Steve分析了美国的Alexa前10名网站的脚本初始需要加载执行情况,发现只有29%是需要页面加载时初始化解析执行的,而其他71%的脚本是在触发交互时才会执行,压缩后这些脚平均加载是229 kB,未压缩是500KB,这是个大量的数据。

我们希望的结果是在页面加载中不去解析执行javascript,只是提前预下载好文件。例如通用的点击弹出二级导航,用户有可能永远都没有点击导航的行为,此时导航的功能脚本根本毫无用处。但是人们在点击导航时不希望等太久javascript的执行,所以ControlJS会提前下载文件,这样javascript只是解析执行,不会花时间放到下载文件上。代码一目了然,具体操作可看Menu WITH ControlJS

重写document.write

在默认情况下这些异步脚本都是在window.onload解析执行,如果此时脚本调用window.write方法,将导致一个不希望发生的问题,就是整个页面被window.write的输出内容替换,所有页面内容将被删除,ie下将处于停滞状态。产生问题的原因是由于在docuemnt被加载完后调用docuemnt.write方法时将会自动去触发document.open,写入任何处于打开状态的doucment都将会会替换整个页面的内容。这便导致目前为止所有异步脚本无法延迟document.write的问题,ControlJS的处理方法是重写docuemnt.write,如下:

CJS.docwriteOrig = document.write;
document.write = CJS.docwrite;

ControlJS创建一个dom元素(span),将其插入当前被解析执行的script标签之前,并且设置SPAN的innerHTML的值为docuemnt.write的内容。具体操作可看document.write WITH ControlJS

  • 用ControlJS优化阿里妈妈广告

对于现在大部分的广告形式都是采用document.write方式写入,无法将这些内容异步处理是开发者非常头疼的问题。ControlJS可以为我们解决这类烦恼。


没有应用ControlJS的网络图。DEMO可以看http://chesihui.github.com/ad-demo.html


应用ControlJS优化后的网络图。DEMO可以看http://chesihui.github.com/ControlJS-demo.html

  • ControlJS的局限性

ControlJS存在一个问题是在document.write中多层嵌套script标签时,页面仍然存在触发document.open的问题。查看源代码发现在执行完一个javascript后都会恢复document.write的原生方法:

document.write = CJS.docwriteOrig;

动态脚本的异步加载,同样使得document.write方法也是异步执行,因此不能恢复document.write的原生功能。复现的情况如DEMO。注释这段脚本虽然解决了不触发window.open的问题,但是同样的异步加载执行导致无法正确定位广告写入的位置。对于阿里妈妈广告设置alimama_type=”i”的时候,载入图片广告是根据多层document.write实现的,只能正确渲染最后一个图片广告。复现如DEMO

因为ControlJS的异步加载不存在任何依赖顺序,所有脚本都是并行加载执行,如果你的页面存在太多依赖关系,ControlJS将不会适合你的项目。

最后总结ControlJS为我们做了什么事,利弊还需要自己去权衡:
1、异步下载所有脚本
2、同时处理内嵌与外链脚本
3、延迟脚本的执行直到页面被渲染完
4、允许脚本只下载不执行
5、解决了异步脚本中存在docuement.write的问题
6、ControlJS本身是异步加载

相关日志

Categories: 技术.

Tags:

Comment Feed

No Responses (yet)



Some HTML is OK

or, reply to this post via trackback.

*
To prove you're a person (not a spam script), type the security word shown in the picture. Click on the picture to hear an audio file of the word.
Click to hear an audio file of the anti-spam word