JavaScript文件的加载总是让人头疼的一件事情.

  最近在从前端性能、SEO和可用性这些方面优化聚尚网,最开始的时候大家都发现网站首页刷新后需要等待一点点时间图片才会出来,让用户感觉图片的载入有明显的延迟,显然用户体验是不佳的。

  网站首页因为图片比较多,大概有180张,因此用了jquery的一个延时加载的插件,具体的说,网站中幻灯片那一栏以下的图片都是通过lazyload这一js动态循环出来的,导致刷新的时候总是能感觉到明显的载入延迟,一开始我们以为是图片没有进缓存,但是通过Firebug分析网站的网络活动的时候,发现图片第二次刷新页面的时候,三分之二的图片接收数据那一阶段已经是0,也就是说,图片是已经进了缓存,而还有另外三分之一的图片没有进缓存,后来发现是CDN那边的问题。

  然而,已经进缓存了,按道理图片的加载应该是非常快的,甚至刷新的时候根本看不出来,为什么还会出现这样的问题?通过Ajax DynaTrace Edition监控发现,主要是Js阻塞了图片的加载,并且lazyload.js也存在一定的问题,执行时间达到了270ms左右,这是不可接受的,js创始人也说过,当一个js执行时间超过了100ms,就应该是什么地方除了什么问题,最终我们也对lazyload.js进行了一个优化,已经将执行时间下调到了40多ms,但效果仍然不明显。

  瓶颈到底又在哪里呢?经分析有两个原因,一个是DOM的渲染,js执行是非常快的,但是改变了DOM,并且是引发repaint和reflow的DOM操作,是非常消耗性能的,也就是说,选择了这种动态载入的机制,就会存在这个瓶颈,这是一定的;还有一个最重要的原因就是被其他的JavaScript文件阻塞了图片文件的加载,我们知道,JavaScript的执行是单线程的,当一个js在执行的时候,不会有其他的UI线程现在处于活动状态,正是因为这样,导致我们图片总是迟迟出现,甚至被人家笑话为圈圈网,因为图片没有出来的时候,img父标签的背景是一个gif的转动圈圈,以暗示用户改图片正在加载,丢人啊。。。

  既然是js文件的问题,我们就目标很明确了,直接把js放到body底部就完事了,然而,事实并没有那么简单,发现18个左右的js文件是分布在6个模版文件中,头部、导航、幻灯片、首页内容、底部、google分析和广告js,而且依赖关系非常强,也就意味着只能在具体的某一个文件内调整js的顺序,这样带来的效果肯定是不佳的,因为我们的图片一般都是在所有的js执行完后才下载,被阻塞的。没有办法,也就只有把js优化的中心放在下载和执行非常耗时的js上,特别是一些远程js,如google分析的ga.js等六七个搜索引擎和广告相关的远程js,像ga.js,是在google的服务器上下载下来的,有时候感觉google就他妈的的被和谐了,总卡在那里,解析个域名要好几秒。如下图:  

  从上图我们可以发现,有几个js下载的时间是有时间间隔的,那是因为上一个js正在执行,执行玩了下一个文件才会开始下载。我们能做的、需要做的就是做到让它同时下载。

  然而怎么去做方便呢,jQuery又一个用于载入js的方法,$.getScript(url,callback),在源码中它是这样定义的,如下图:

  getScript: function( url, callback ) {
        return jQuery.get(url, null, callback, "script");
    },

  get: function( url, data, callback, type ) {
        // shift arguments if data argument was omited
        if ( jQuery.isFunction( data ) ) {
            type = type || callback;
            callback = data;
            data = null;
        }

        return jQuery.ajax({
            type: "GET",
            url: url,
            data: data,
            success: callback,
            dataType: type
        });
    }  

  if ( s.dataType === "script" && type === "GET" && remote ) {
            var head = document.getElementsByTagName("head")[0]   ||document.documentElement;
            var script = document.createElement("script");
            script.src = s.url;
            if ( s.scriptCharset ) {
                script.charset = s.scriptCharset;
            }

  从以上代码我们可以看出,该方法是通过ajax实现的,并只针对远程js文件,把它动态载入页面,《高性能JavaScript》一书的无阻塞加载那一章节也提到过这一方式。当我把那几个网站分析和广告的js换成这种方式加载后,发现了明显的变化,如下图:

  这几个js都同时下载开始下载了,Ajax动态载入远程js文件果然给力啊!!!

曾祥瑚 2011-11-23