1. 发现问题
最近在写年终总结的时候发现文章链接使用链接卡片时,文章的封面图不能加载,显示效果如下。

但是你看,图片明明是可以加载的,问题出在哪里?

2. 分析问题
图片是可以加载的,下面的<hyperlink-card>标签就是链接卡片,框出来的部分就是本该显示出来的图片。
很显然,问题出在referrerpolicy="no-referrer"上了。

referrerpolicy用于控制浏览器发送http请求时如何携带Referer信息的,"no-referrer"就表示完全禁止发送Referer头部。当链接卡片加载图片时,浏览器不会将当前页面的地址告诉目标服务器,对于本站来讲就是腾讯云对象存储COS。
腾讯云对象存储COS的防盗链设置中,我是设置了拒绝空Referer的。

如果允许空Referer的话,referrerpolicy="no-referrer"肯定能加载图片,但这就不安全了。
3. 解决问题
懒得去分析链接卡片为什么是no-referrer的,只要把referrerpolicy="no-referrer"改成referrerpolicy="strict-origin-when-cross-origin"不就解决问题了嘛,在跨域请求时只发送源作为Referer。
先试验一下,获取class="usp-n3v8cb"的img元素,获取到的是一个空的NodeList。为什么呢?

<hyperlink-card>标签是链接卡片,我注意到在它的下面有个#shadow-root (open),这是个什么?
在Web开发中,#shadow-root (open)是Shadow DOM的根节点。Shadow DOM是一种封装技术,允许将DOM树附加到常规DOM树中的某个元素上,但保持其内部结构的私有性,从而实现样式和行为的封装。
Shadow DOM:它是Web组件标准的一部分,用于创建封装的“影子”DOM树,该树附加到常规DOM元素(称为影子宿主)上,但与其子代分开渲染。这意味着Shadow DOM内部的样式和脚本不会影响到外部DOM,外部的样式也不会影响到Shadow DOM内部(除了某些特定的CSS属性,如继承属性)。
#shadow-root:当为一个元素创建Shadow DOM时,该元素下会附加一个shadow root,即影子根。它是Shadow DOM树的根节点。
open:表示这个Shadow DOM是开放的,可以通过JavaScript访问。例如,通过element.shadowRoot可以获取到该影子根并操作其内部节点。
closed:与之相对的是closed模式,即影子根对外部是关闭的,element.shadowRoot会返回null,因此外部无法直接访问Shadow DOM内部。
以上是来自Deepseek的介绍,可以看出这种Shadow DOM不会影响到外部DOM,也不受外部影响,所以上文的document.querySelectorAll('img.usp-n3v8cb')是无法获取到<img>元素的。
但是对于开放的Shadow DOM,是可以通过element.shadowRoot获取的,来试一下。

可以看出,这个思路是可行的,referrerpolicy的值被变更成strict-origin-when-cross-origin。

代码如下,只需要把如下代码用forEach循环实现就行了。
document
.querySelectorAll('hyperlink-card')[0]
.shadowRoot
.querySelectorAll('img.usp-n3v8cb[referrerpolicy="no-referrer"]')[0]
.setAttribute('referrerpolicy', 'strict-origin-when-cross-origin')完整的js代码如下,使用forEach替换每一个hyperlink-card下的img。
考虑到Shadow DOM的渲染需要额外时间,即使在DOMContentLoaded事件触发后,Shadow DOM内部的元素也可能尚未完全渲染完成,所以加了一个500ms的延迟。500ms是一个相对安全的等待时间,足够覆盖大多数浏览器的渲染周期,既不会影响用户体验,又能确保Shadow DOM已渲染完成,实际体验下来也是确实可行的。
// 解决链接卡片图片加载时不发送Referer头
document.addEventListener('DOMContentLoaded', function() {
setTimeout(() => {
const hyperlinks = document.querySelectorAll('hyperlink-card');
hyperlinks.forEach(link => {
if (!link.shadowRoot || link.shadowRoot.mode !== 'open') return;
const imgs = link.shadowRoot.querySelectorAll('img.usp-n3v8cb[referrerpolicy="no-referrer"]');
imgs.forEach(img => {
img.setAttribute('referrerpolicy', 'strict-origin-when-cross-origin');
});
});
}, 500);
});看看最终效果。

以上。