Administrator
发布于 2026-03-06 / 1 阅读
0
0

H5预加载资源link、script属性对跨域调用的影响

引言

近来发现一个浏览器缓存策略影响h5页面调用java script的现象,引发如下问题的验证。

  1. h5使用 <link rel=“preload” href="{{ js_url }}" as=“script” {{ crossorigin_attr }}>预加载js文件后,为什么浏览器会在script标签处再次发起文件加载。
  2. 为什么偶现再次加载时可能会在console报错(域名已经在测试环境替换为localhost):Access to script at ‘http://localhost:5001/test.js’ from origin ‘http://localhost:5000’ has been blocked by CORS policy: The ‘Access-Control-Allow-Origin’ header contains multiple values ‘http://localhost:5000,http://example.com’, but only one is allowed.

当link标签的属性rel是preload时,必须包含 as 属性,可选 crossorigin 属性,crossorigin属性的值可为 anonymoususe-credentials

crossorigin属性为空

浏览器发送请求时不携带origin头
服务器有可能不返回Access-Control-Allow-Origin,也可能返回值为多个域

crossorigin="anonymous"(匿名模式)

浏览器发送跨域请求时不携带用户凭证(如 Cookies、HTTP 认证信息、TLS 客户端证书等)。
服务器只需返回 Access-Control-Allow-Origin: *(或具体源)即可,无需返回 Access-Control-Allow-Credentials: true

crossorigin="use-credentials"(凭证模式)

浏览器发送跨域请求时携带用户凭证(Cookies、HTTP 认证等)
服务器必须返回 Access-Control-Allow-Credentials: true,且Access-Control-Allow-Origin 不能为 *,必须指定具体的源(如 https://your-domain.com)。

script element标签元素的属性

crossorigin属性与link标签的属性值一致,参考上一节说明

关于引言中问题1的实验

第1次触发下载的原因分析

当link标签内容是:<link rel="preload" href="http://localhost:5001/test.js" as="script" >
它没有crossorigin属性,此时浏览器发出预加载请求时不会携带origin头。相应的,服务器有两种场景:

  1. 不会响应Access-Control-Allow-Origin头
  2. Access-Control-Allow-Origin头的值不是确定的域或*
    由于本服务器配置的是多个域,那么他会直接响应:Access-Control-Allow-Origin: http://localhost:5000,http://example.com
    注:这是符合协议规范的
    image-hWoh.png

再来看第2次资源下载的原因分析

参考网络资料的说明:由于在script标签设定了crossorigin属性,因为 <link rel="preload"><script> 标签的 crossorigin 属性不一致,导致浏览器无法复用缓存,从而发起两次网络请求。
<script src="http://localhost:5001/test.js" crossorigin="anonymous"></script>

关于引言中问题2的实验(CORS 错误)

第2次 test.js 资源下载请求,实际没有发出网络请求,是命中了浏览器缓存

那么既然在h5页面中 link、script标签的crossorigin属性不一致时,会认为时不一样的资源,为什么浏览器实际还是命中缓存了,参考如下
实际上这次并没有真的发出网络请求,由于第一次下载响应了Cache-Control: max-age=10浏览器命中了缓存文件,证据如下
image-yHLG.png
服务端日志,这个时段只收到1次下载请求
image-yHAu.png

参考网络资料得出:
虽然 Fetch 规范建议根据请求的凭证模式(credentials mode)区分缓存条目,但实际浏览器实现中,如果服务器响应允许公开缓存(public),并且没有依赖 Cookie 或 Origin 的差异,缓存条目可以被不同凭证模式的请求复用。这属于一种优化行为,但并非所有浏览器都一致,也可能受到具体版本的影响。
即受Cache-Control响应头的影响,验证测试响应为no-cache时,第二次加载(script加载)无法命中缓存,且由于有crossorigin="anonymous"属性,可以正常拿到资源,且无跨域报错
image-coyR.png

那为什么说是偶现的呢?

在浏览器devtools的network调试页面中,通常会勾选disable cache,就模拟出了响应头Cache-Control: no-cache的效果

正确的预加载方案

在link和script标签中设定相同的跨域属性,既能保证正常跨域,又能减少资源加载次数、节省带宽
<link rel="preload" href="http://localhost:5001/test.js" as="script" crossorigin=&#34;anonymous&#34;>

<script src="http://localhost:5001/test.js" crossorigin=&#34;anonymous&#34;></script>

image-kBtT.png


评论