前端优化之js资源加载策略

@kuitos 2015-08-23 09:37:04发表于 kuitos/kuitos.github.io javascript性能优化

前端优化之js资源加载策略

原文写于 2014-08-13

阅读这篇blog之前,请先看下这本书:高性能网站建设进阶指南,里面详细的讲解了现今流行的几种异步脚本加载方案(不过里面一些结论不能盲目相信,实践之前请手动验证一下,毕竟浏览器实现日新月异)
不过还是先简单介绍下两种最常用的动态加载js资源的方案:

  • document.write方式

    function outerHTML (node) {
                // if IE, Chrome take the internal method otherwise build one
                return node.outerHTML || (function (n) {
                    var div = document.createElement('div'), h;
                    div.appendChild(n);
                    h = div.innerHTML;
                    div = null;
                    return h;
                })(node);
            };
    
    document.write(outerHTML(el));
  • script dom element方式

    document.getElementsByTagName('head')[0].appendChild(el);

两种方式的差异在这里

是否并行下载 执行是否保证顺序
doc write
script dom element

差别只在于,script dom element 的策略是并行下载且谁先下完执行谁,不根据你script标签的顺序加载,真正的异步。
因此我们可以有个基础的认识,就是当前页面一定会用到的js,我们采用doc write方式去加载,而用不到的js就采用script dom element 方式

看一下案例

这里有两个问题:

  1. h.js没有跟其他js一起并行下载,明显被阻塞了。
  2. 第二张图的ajax请求几乎是最后才触发的,但是这个请求是最关键所在,它会向后台拿数据然后渲染到前台。也就是说在他的相应回来之前,页面会一直出于无数据(用户看上去就好像没打开完成一样)状态,h.js是百度统计需要的js,并不是我们立即要用到的,理应在最后才加载。而查后台数据的ajax请求是页面渲染的关键,应该尽可能的在必需js加载完之后立即执行,这样至少页面看上去会打开快点(页面渲染的时间提前)。

基于这两个问题,我们的思路是这样的:

  1. 不必要的资源我们做延时载入,这里使用setTimeout,尽量使这些文件在所有必需请求完成之后进行,然后偷偷下载。
  2. 尽可能提前请求数据的ajax.
    这里需要提到一点知识,就是javascript是单线程的,也就是说如果有一个js在执行占用了线程,那么其他js就不能获得执行的机会,必须等待前一个执行完毕。
    基于这个事实,采用setTimeout将不需要首页加载的js延时到最后执行,避免去跟主js抢占线程是最合适且最简单的方案。(不采用按需加载的方案是有综合考虑的,如压缩、代价、耗时等,因情况而定,具体可以私聊)

优化后的部分代码:

<script>
    (function (window, ScriptLoader) {
        ScriptLoader.addScriptsSync(["content.base.min.js", "content.app.min.js"]);
    })(window, window.ScriptLoader);
</script>
<!--不需要立即加载的延迟脚本放这里-->
<script>
    (function (ScriptLoader) {
        ScriptLoader.addScriptAsyncDelayed(["content.lib.min.js",
                    (("https:" == document.location.protocol) ? "https://" : "http://") + "hm.baidu.com/h.js?30219399d7b243256f05f99e96aadb68"], null);
    })(window.ScriptLoader);
</script>

看下效果:

可以看到请求数据的ajax被提前了,有了数据页面会立即渲染更新,所以这里页面展示完全的时间在 1.7s左右,优化前在2.5s左右。
content.lib.min.js和h.js作为首页不会立即用到的东西,在最后才做加载且下载是并行的。

汇总一下优化历程

js/css简单压缩合并 服务器开启gzip 文中优化策略
优化前 请求数 70+,资源总大小2M左右,页面打开耗时5s+ 请求数 20+,资源总大小1.2M,页面打开耗时3s左右 同左
优化后 请求数 20+,资源总大小1.2M,页面打开耗时3s左右 请求数 20+,资源总大小350K,页面打开耗时2.5s左右 资源请求不变,页面打开耗时 1.7s

关于ScriptLoader具体实现,没什么高科技,有兴趣同学可以看下代码:ScriptLoader

Ps:当页面耗时已经在2秒左右时,稳定提升100ms都是有难度的,这就好比110米栏选手跑进14秒容易,跑进13秒就很难了!