动态添加的为什么没触发预加载?
我在单页应用里想用JavaScript动态预加载字体文件,但发现资源始终没有被浏览器预加载。检查过网络面板,确实没有请求发出,代码逻辑看起来没问题,这是怎么回事?
我尝试过这样写:
document.addEventListener('DOMContentLoaded', () => {
const link = document.createElement('link');
link.rel = 'preload';
link.href = '/fonts/myfont.woff2';
document.head.appendChild(link);
});
但字体加载时还是出现了FOIT闪烁。难道动态添加的preload需要额外设置什么属性吗?手动在HTML里写静态的就能生效,动态添加就失效了?
解决办法是别等 DOMContentLoaded,越早越好,最好在脚本一执行就插入。你可以直接把这段逻辑放在一个内联 script 里,不要加任何事件监听:
注意关键点:
- 必须加
link.as = 'font',告诉浏览器这是字体资源- 跨域字体一定要设
crossOrigin,不然可能加载失败但不报错- 最好放在 head 里的内联脚本执行,不要等任何事件
如果你实在需要用动态时机控制,至少用
document.head.insertBefore(link, document.head.firstChild)插到 head 最前面,提高被识别的概率。我试过这方法在 Vue、React 的 SPA 里都能让 woff2 提前进预加载队列,FOIT 明显减少。希望能帮到你。
preload确实有点坑,前端这块不少人都遇到过类似问题。主要原因是浏览器对动态插入的资源加载有优化策略,可能不会立刻执行预加载。你这段代码逻辑本身没啥问题,但少了关键的
as属性。对于字体文件,浏览器需要明确知道你要预加载的是什么类型的资源。所以你需要加上as="font"和crossorigin="anonymous",像这样:另外,FOIT(Flash of Invisible Text)的问题通常和字体加载策略有关。你可以考虑用 CSS 的
font-display属性来优化显示行为,比如设置为swap或optional:最后提醒一下,动态 preload 虽然方便,但有些浏览器可能会稍微延迟处理这些请求,建议能静态写在 HTML 里的尽量静态写。如果必须动态加载,确保所有属性都配齐了,尤其是
as和crossorigin。