我有一个大的 JSON blob,我想在我的网页中预加载。为此,我已将
<link rel="preload" as="fetch" href="/blob.json">
添加到我的页面。我也有一个 JS 请求来获取相同的 blob。
这不起作用,控制台报告:
[警告] 资源 blob.json 已使用链接预加载进行预加载,但在窗口加载事件后的几秒钟内未使用。请确保它没有被无故预加载。
MDN 声称可以通过在链接标签中添加
crossorigin
来解决此问题。 AFAICT,这不是真的,任何组合或跨域属性都不会真正使其发挥作用。
使用开发人员控制台中的 copy-as-curl 命令,似乎没有链接标记加属性的组合可以发出与 JS 中的 fetch/XHR 调用相同的请求。
我很乐意在这一点上犯错。
这里是预加载提取的工作解决方案,它可以在 Chrome 和 Safari 中运行并支持 cookie。
不幸的是,它仅适用于相同的域请求。
首先,不要为预加载标签指定
crossorigin
属性,这将确保Safari将以no-cors
模式发送请求并包含cookie
<link rel="preload" as="fetch" href="/data.json">
其次,fetch api 请求也应该在
no-cors
模式下完成并包含凭据(cookie)。
请注意,此请求不能有任何自定义标头(如 Accept
、Content-Type
等),否则浏览器将无法将此请求与预加载的标头匹配。
fetch('/data.json', {
method: 'GET',
credentials: 'include',
mode: 'no-cors',
})
我尝试过
crossorigin
属性值和 fetch API 配置的其他组合,但它们在 Safari 中都不起作用(仅在 Chrome 中)。
这是我尝试过的:
<link rel="preload" as="fetch" href="/data.json" crossorigin="anonymous">
<script>
fetch('/data.json', {
method: 'GET',
credentials: 'same-origin',
mode: 'cors',
})
</script>
上面的方法在 Chrome 中有效,但在 Safari 中无效,因为 Safari 中的预加载请求不会发送 cookie。
<link rel="preload" as="fetch" href="/data.json" crossorigin="use-credentials">
<script>
fetch('/data.json', {
method: 'GET',
credentials: 'include',
mode: 'cors',
})
</script>
以上内容适用于 Chrome,但不适用于 Safari。虽然 cookie 是通过预加载请求发送的,但 Safari 无法将 fetch 与预加载请求匹配,可能是因为 cors 模式不同。
感谢这个 bug 中的讨论,我让
fetch
可以在 Chromium 67 中与 preload
一起工作:
首先,给预加载链接添加crossorigin属性:
<link rel="preload" as="fetch" href="/blob.json" crossorigin="anonymous">
其次,将同源凭证添加到获取请求中:
fetch(url, {credentials: 'same-origin'}).then(response => {
console.log(response);
});
或者,您可以使用
XMLHttpRequest
代替 fetch
(使用 XHR,您不需要向请求添加任何内容),但前提是您不打算使用 responseType = 'blob'
- 它不会工作,因为另一个错误。
看起来这是 Safari 和 Chrome 之间的区别。 Safari 将警告发布到控制台,但 Chrome 没有,所以也许向链接元素添加
crossorigin
确实可以解决问题,但 Safari 有某种错误?
添加尤金上面的答案(我将其添加为评论,但缺乏声誉)。
此案例适用于 Safari 中的跨域请求(但不适用于 Chrome)。
<link rel="preload" as="fetch" href="https://someotherorigin.com/data.json" crossorigin="anonymous">
<script>
fetch('https://someotherorigin.com/data.json', {
method: 'GET',
credentials: 'omit',
mode: 'cors',
})
</script>