Vue 中 img 缓存导致的 fetch 跨域问题,别再只盯着 crossorigin 了
Vue 项目中图片跨域问题排查与解决
问题背景
最近在开发一个 Vue 项目时,遇到一个诡异的问题:页面上通过 <img> 标签加载的图片,在浏览器缓存之后,通过 JavaScript 的 fetch 方法再次获取时,会直接爆跨域错误。
这个问题很奇怪——图片明明已经在本地缓存了,为什么还会跨域报错?
问题复现
// 页面上的图片
<img src="https://cdn.example.com/image.jpg" id="myImage" />
// 通过 JS 获取图片
async function getImage() {
const img = document.getElementById('myImage');
const response = await fetch(img.src); // 这里爆跨域错误
return response.blob();
}
错误信息类似:
Access to fetch at 'https://cdn.example.com/image.jpg' from origin 'https://your-site.com'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
问题根因
要理解这个问题,首先要搞清楚 crossorigin 属性的默认值。
很多人以为 crossorigin="anonymous" 是默认值,其实是错的。
真正的情况是:
| 属性 | 含义 |
|---|---|
| 不写 crossorigin 属性 | 不携带跨域凭证,不带 Origin 请求头 |
crossorigin="anonymous" |
携带跨域凭证,发送 Origin 请求头 |
crossorigin=""(空字符串) |
携带跨域凭证,发送 Origin 请求头 |
crossorigin(只有属性名,无值) |
携带跨域凭证,发送 Origin 请求头 |
crossorigin="use-credentials" |
携带跨域凭证 + Cookie,发送 Origin 请求头 |
只要写了 crossorigin 属性(无论值是什么),img 就会携带 Origin 请求头。 不写这个属性,才不会携带。
问题的触发需要同时满足两个条件:
<img>标签没有 crossorigin 属性:img 不带Origin请求头- 服务器设置了图片缓存(Cache-Control):图片被浏览器缓存
两者缺一不可:
- 没有缓存:fetch 直接发新请求,服务器正常处理(或直接报错,错误信息清晰)
- 有 crossorigin 属性:请求带 Origin,服务器返回 CORS 头,缓存里包含跨域信息,fetch 正常
- 没有 crossorigin 属性 + 有缓存:img 加载时没带 Origin → 服务器没返回 CORS 头 → 图片被缓存时没有跨域信息 → fetch 读取缓存 → 跨域检查失败
简单来说:缓存把没有 CORS 头的信息也一起存下来了,后续 fetch 访问时,浏览器发现响应里缺少 Access-Control-Allow-Origin,于是判定跨域失败。
解决方案
给 <img> 标签加上 crossorigin="anonymous" 属性:
<img
src="https://cdn.example.com/image.jpg"
id="myImage"
crossorigin="anonymous"
/>
加上这个属性后:
<img>发起的 HTTP 请求会带上Origin: https://your-site.com请求头- CDN 服务器检测到
Origin请求头,返回响应时会在响应头加上Access-Control-Allow-Origin: *(或对应的域名) - 图片连同正确的跨域响应头一起被缓存
- 后续
fetch()访问时,浏览器检查缓存响应的 CORS 头,通过验证
完整示例
// 页面 HTML
<img
src="https://cdn.example.com/image.jpg"
id="myImage"
crossorigin="anonymous"
/>
// JS 代码
async function getImage() {
const img = document.getElementById('myImage');
const response = await fetch(img.src);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const blob = await response.blob();
return URL.createObjectURL(blob);
}
注意事项
-
服务器端需要支持 CORS:CDN 或图片服务器必须返回
Access-Control-Allow-Origin响应头,否则即使用了crossorigin属性也没用。 -
缓存需要清除:解决问题后,需要清除浏览器缓存,否则旧的没有跨域头的缓存还在,问题依然存在。
-
第三方图片:如果图片托管在第三方平台(如七牛、阿里云 OSS、Cloudflare 等),确保它们已正确配置 CORS 响应头。
总结
这个问题本质上是 HTTP 缓存机制与 CORS 机制的交互。<img> 标签默认不带跨域信息,导致缓存的图片缺少 CORS 响应头,而 fetch API 需要这些头。
值得注意的是,这个问题在网络上几乎找不到相关讨论。 大部分 CORS 相关文章都是围绕 API 请求或字体文件展开,涉及到 <img> 缓存与 fetch 跨域交互的场景非常罕见。这也让问题的排查变得困难——报错信息指向跨域,但图片明明已经成功加载了,容易让人摸不着头脑。
解决方案很简单:在 <img> 标签上加 crossorigin="anonymous"。让浏览器在请求时带上 Origin 头,服务器返回对应的 CORS 响应头,缓存里就有了跨域信息,后续 fetch 就能正常访问了。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)