Please enable JavaScript to view the comments powered by Disqus.

jquery.min.js的async加载

作为有点强迫症的人,看见站点速度评测网站给你的博客加载扣了分,原因是jquery.min.js导致了一大票js是同步加载,这怎么能忍?

试试defer

defer属性表示,这个脚本将在页面解析完毕之后,DOMContentLoaded触发之前执行(MDN是这么说的,粗略扫了一下HTML5 spec并没有提到defer执行顺序的事情)。看起来很美好,但是其支持不如async广泛,而且内嵌script也没法用了。除此之外,你得清楚地知道script间的依赖关系,否则会和async一样出现xxx is undefined这样的事情。

SO大法好!

以下代码源自SO的这篇回答

如果直接把jquery.min.js和相关脚本打上async,那么:

1
2
Uncaught ReferenceError: $ is not defined
at blahblah.html:3

原来async脚本的执行顺序没有任何保障,惨痛的教训。

作为本博客主题的维护者,我拥有全部javascript代码的控制权,因此我知道我的代码只是依赖$(function() {...})的。既然只用到了$(...),那么我们就做一个假的$出来,把传给它的参数保存起来,到真正的jQuery加载完成之后再再apply给真正的jQuery,岂不美哉?

保证这段代码在所有使用到了$的脚本之前执行:

1
2
3
4
5
6
if(!("jQuery" in window)) {
window._jqQ = []; // jQuery call queue
window.jQuery = window.$ = function() {
_jqQ.push(arguments);
};
}

然后想办法让jQuery加载完成时执行这段脚本(我选择使用scriptonload):

1
2
3
4
5
_jqQ.forEach(function(args) {
$.apply($,args);
});

delete window._jqQ;

本方法的缺点

首先,所有jQuery的调用都得包装在$(function() {...})里面,这个无所谓了,但是$(document).ready这种写法是不行的,因为我们的假$对象中没有任何jQuery的API。

其次,还是因为API的缘故,那种使用prototype来做jQuery扩展的方法就废掉了(jQuery.fn就是它的prototype),你只能找个替代品。我的Bootstrap scrollspy啊,就这么说再见了

好孩子不要学

从炫技的角度出发,丧心病狂地使用bind来避免在forEach部分写个function,one liner万岁!

1
_jqQ.forEach($.apply.bind($, $));

其实C++出身的我为了想明白上面那段js代码的this问题,头都炸了

然后看了一篇关于function, callbindbenchmark评测,以及从可读性角度考虑,果断放弃炫技代码,朴素才是真啊。

参考文献

  1. <script>: The Script element
  2. The script element
作者:Dr. A. Clef
发布日期:2018-04-02
修改日期:2018-04-21
发布协议: BY-SA