jQuery的append引起的坑

今天上午同事反馈一个诡异的问题:

某项目中动态引用的js文件每次请求都会带上临时的时间戳参数,导致缓存无效。非常影响用户体验,因为每次访问都会重新加载大量的js文件

同事找了半天找不到是在哪里添加了时间戳参数,我抽出时间分析了下,经过步步排查,找到问题所在的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var JS_PATH_LIST = [
"bower_components/angular/angular.js",
"bower_components/angular-aria/angular-aria.js",
......
];

$(document).ready(function () {
loadScript(JS_PATH_LIST);
});

function loadScript(paths) {
var divString = '';
for (var i = 0; i < paths.length; i++) {
divString += '<script async defer src="' + paths[i] + '?v=' + SYSTEM_VERSION + '"></script>';
}
$(document.body).append(divString);
}

这段代码看上去没毛病,可实际在执行过程中的请求如下:

image-20220729181624482

所有js文件的请求都额外加上了类似”_=1659089706720“的时间戳参数。

翻遍所有的代码,都没有找出是谁在什么地方添加了这个参数,只好逐个击破。

逐步去掉所有与此函数无关的代码,发现问题依旧会存在,最后只留了最简单的示例代码,问题还是存在,只好怀疑jQuery干了什么事!

把疑问告诉同事,同事一会发来一个参考:

网上给出的方案是:

jQuery.ajaxSetup中设置cache为true

再次查看我们的网络请求,发现确实通过jQuery.append()方式添加的js文件,都是xhr请求;而通过原生JavaScript的appendChild添加的js文件,则是常规的Script请求。

这就充分说明了,jQuery认为这是一个ajax请求,为了防止缓存问题,自动添加了时间戳参数(查看代码也是如此)。而在我们的这个场景中,我们是不想每次都重新请求的。

但是单纯的对我们的场景设置为true会不会有影响呢?查看jQuery官方文档:

  • cache (default: true, false for dataType 'script' and 'jsonp')

    Type: Boolean

    If set to false, it will force requested pages not to be cached by the browser. Note: Setting cache to false will only work correctly with HEAD and GET requests. It works by appending “_={timestamp}” to the GET parameters. The parameter is not needed for other types of requests, except in IE8 when a POST is made to a URL that has already been requested by a GET.

从这段话看出来,属性cache默认为true,但当dataType为script或jsonp时,该属性则默认为false。

而我们的情况,正好是后者,dataType为script,cache为false。所以设置cache为true,强制所有的请求设置了缓存。

但很明显这样做了的话,对于jsonp的请求可能会有缓存问题。好在我们并没有使用json请求,问题算勉强解决。

不过有另外一个问题还没想明白,为什么jQuery.append的angularjs文件可以正常运行,但是通过原生document.appendChild添加的angular文件却执行报错,始终报module not found,网络请求里看到的js文件都是正常请求了的。