使用preload预加载页面资源时注意事项


Posted in Javascript onFebruary 03, 2020

preload 提供了一种声明式的命令,让浏览器提前加载指定资源(加载后并不执行),在需要执行的时候再执行。提供的好处主要是

将加载和执行分离开,可不阻塞渲染和 document 的 onload 事件

提前加载指定资源,不再出现依赖的 font 字体隔了一段时间才刷出

如何使用 preload

使用 link 标签创建

<!-- 使用 link 标签静态标记需要预加载的资源 -->
<link rel="preload" href="/path/to/style.css" rel="external nofollow" as="style">
<!-- 或使用脚本动态创建一个 link 标签后插入到 head 头部 -->
<script>
const link = document.createElement('link');
link.rel = 'preload';
link.as = 'style';
link.href = '/path/to/style.css';
document.head.appendChild(link);
</script>

使用 HTTP 响应头的 Link 字段创建

Link: <https://example.com/other/styles.css>; rel=preload; as=style

如我们常用到的 antd 会依赖一个 CDN 上的 font.js 字体文件,我们可以设置为提前加载,以及有一些模块虽然是按需异步加载,但在某些场景下知道其必定会加载的,则可以设置 preload 进行预加载,如:

<link rel="preload" as="font" href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >
<link rel="preload" as="script" href="https://a.xxx.com/xxx/PcCommon.js" rel="external nofollow" >
<link rel="preload" as="script" href="https://a.xxx.com/xxx/TabsPc.js" rel="external nofollow" >

如何判断浏览器是否支持 preload

目前我们支持的浏览器主要为高版本 Chrome,所以可放心使用 preload 技术。 其他环境在 caniuse.com 上查到的支持情况如下:

使用preload预加载页面资源时注意事项

在不支持 preload 的浏览器环境中,会忽略对应的 link 标签,而若需要做特征检测的话,则:

const isPreloadSupported = () => {
const link = document.createElement('link');
const relList = link.relList;
if (!relList || !relList.supports) {
return false;
}
return relList.supports('preload');
};

如何区分 preload 和 prefetch

preload 是告诉浏览器页面必定需要的资源,浏览器一定会加载这些资源;

prefetch 是告诉浏览器页面可能需要的资源,浏览器不一定会加载这些资源。

preload 是确认会加载指定资源,如在我们的场景中,x-report.js 初始化后一定会加载 PcCommon.js 和 TabsPc.js, 则可以预先 preload 这些资源;

prefetch 是预测会加载指定资源,如在我们的场景中,我们在页面加载后会初始化首屏组件,当用户滚动页面时,会拉取第二屏的组件,若能预测用户行为,则可以 prefetch 下一屏的组件。

preload 将提升资源加载的优先级

使用 preload 前,在遇到资源依赖时进行加载:

使用preload预加载页面资源时注意事项

使用 preload 后,不管资源是否使用都将提前加载:

使用preload预加载页面资源时注意事项

可以看到,preload 的资源加载顺序将被提前:

使用preload预加载页面资源时注意事项

避免滥用 preload

使用 preload 后,Chrome 会有一个警告:

使用preload预加载页面资源时注意事项

如上文所言,若不确定资源是必定会加载的,则不要错误使用 preload,以免本末倒置,给页面带来更沉重的负担。

当然,可以在 PC 中使用 preload 来刷新资源的缓存,但在移动端则需要特别慎重,因为可能会浪费用户的带宽。

避免混用 preload 和 prefetch

preload 和 prefetch 混用的话,并不会复用资源,而是会重复加载。

<link rel="preload" href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" as="font">
<link rel="prefetch" href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" as="font">

使用 preload 和 prefetch 的逻辑可能不是写到一起,但一旦发生对用一资源 preload 或 prefetch 的话,会带来双倍的网络请求,这点通过 Chrome 控制台的网络面板就能甄别:

使用preload预加载页面资源时注意事项

避免错用 preload 加载跨域资源

若 css 中有应用于已渲染到 DOM 树的元素的选择器,且设置了 @font-face 规则时,会触发字体文件的加载。 而字体文件加载中时,DOM 中的这些元素,是处于不可见的状态。对已知必加载的 font 文件进行预加载,除了有性能提升外,更有体验优化的效果。

在我们的场景中,已知 antd.css 会依赖 font 文件,所以我们可以对这个字体文件进行 preload:

<link rel="preload" as="font" href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >

然而我发现这个文件加载了两次:

使用preload预加载页面资源时注意事项

使用preload预加载页面资源时注意事项

使用preload预加载页面资源时注意事项

原因是对跨域的文件进行 preload 的时候,我们必须加上 crossorigin 属性:

<link rel="preload" as="font" crossorigin href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >

再看一下网络请求,就变成一条了。

W3 规范是这么解释的:

Preload links for CORS enabled resources, such as fonts or images with a crossorigin attribute, must also include a crossorigin attribute, in order for the resource to be properly used.

那为何会有两条请求,且优先级不一致,又没有命中缓存呢?这就得引出下一个话题来解释了。

不同资源加载的优先级规则

我们先来看一张图:

使用preload预加载页面资源时注意事项

这张表详见:Chrome Resource Priorities and Scheduling

这张图表示的是,在 Chrome 46 以后的版本中,不同的资源在浏览器渲染的不同阶段进行加载的优先级。 在这里,我们只需要关注 DevTools Priority 体现的优先级,一共分成五个级别:

Highest 最高

Hight 高

Medium 中等

Low 低

Lowest 最低

使用preload预加载页面资源时注意事项

html 主要资源,其优先级是最高的

使用preload预加载页面资源时注意事项

使用preload预加载页面资源时注意事项

css 样式资源,其优先级也是最高的

使用preload预加载页面资源时注意事项

CSS(match) 指的是对已有的 DOM 具备规则的有效的样式文件。

使用preload预加载页面资源时注意事项

script 脚本资源,优先级不一

使用preload预加载页面资源时注意事项

使用preload预加载页面资源时注意事项

前三个 js 文件是写死在 html 中的静态资源依赖,后三个 js 文件是根据首屏按需异步加载的组件资源依赖,这正验证了这个规则。

font 字体资源,优先级不一

使用preload预加载页面资源时注意事项

使用preload预加载页面资源时注意事项

样式文件中有一个 @font-face 依赖一个 font 文件,样式文件中依赖的字体文件加载的优先级是 Highest; 在使用 preload 预加载这个 font 文件时,若不指定 crossorigin 属性(即使同源),则会采用匿名模式的 CORS 去加载,优先级是 High,看下图对比: 第一条 High 优先级也就是 preload 的请求:

使用preload预加载页面资源时注意事项

第二条 Highest 也就是样式引入的请求:

使用preload预加载页面资源时注意事项

可以看到,在 preload 的请求中,缺少了一个 origin 的请求头字段,表示这个请求是匿名的请求。 让这两个请求能共用缓存的话,目前的解法是给 preload 加上 crossorigin 属性,这样请求头会带上 origin, 且与样式引入的请求同源,从而做到命中缓存:

<link rel="preload" as="font" crossorigin href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >

这么请求就只剩一个:

使用preload预加载页面资源时注意事项

使用preload预加载页面资源时注意事项

在网络瀑布流图中,也显示成功预加载且后续命中缓存不再二次加载:

使用preload预加载页面资源时注意事项

总结

preload 是个好东西,能告诉浏览器提前加载当前页面必须的资源,将加载与解析执行分离开,做得好可以对首次渲染带来不小的提升,但要避免滥用,区分其与 prefetch 的关系,且需要知道 preload 不同资源时的网络优先级差异。

preload 加载页面必需的资源如 CDN 上的字体文件,与 prefetch 预测加载下一屏数据,兴许是个不错的组合。

更多关于preload预加载页面的文章大家可以看看下面的相关链接

Javascript 相关文章推荐
[原创]图片分页查看
Aug 28 Javascript
JavaScript高级程序设计 扩展--关于动态原型
Nov 09 Javascript
jquery根据name属性查找的小例子
Nov 21 Javascript
JS实现统计复选框选中个数并提示确定与取消的方法
Jul 01 Javascript
js时钟翻牌效果实现代码分享
Jul 31 Javascript
javascript同步服务器时间和同步倒计时小技巧
Sep 24 Javascript
详解使用grunt完成requirejs的合并压缩和js文件的版本控制
Mar 02 Javascript
jQuery设置图片等比例缩小的方法
Apr 29 jQuery
angularJS模态框$modal实例代码
May 27 Javascript
js判断用户是输入的地址请求的路径(实例讲解)
Jul 18 Javascript
详解Angular如何正确的操作DOM
Jul 06 Javascript
Javascript异步编程async实现过程详解
Apr 02 Javascript
jQuery实现小火箭返回顶部特效
Feb 03 #jQuery
JS常用正则表达式超全集(密码强度校验,金额校验,IE版本,IPv4,IPv6校验)
Feb 03 #Javascript
微信小程序实现上传多个文件 超过10个
Mar 30 #Javascript
微信小程序实现同时上传多张图片
Feb 03 #Javascript
解决小程序无法触发SESSION问题
Feb 03 #Javascript
vue组件创建的三种方式小结
Feb 03 #Javascript
Vuex模块化应用实践示例
Feb 03 #Javascript
You might like
不用GD库生成当前时间的PNG格式图象的程序
2006/10/09 PHP
PHP 万年历实现代码
2012/10/18 PHP
thinkPHP+ajax实现统计页面pv浏览量的方法
2017/03/15 PHP
通过PHP设置BugFree获取邮箱通知
2019/04/25 PHP
jquery中$.post()方法的简单实例
2014/02/04 Javascript
用jQuery实现的智能隐藏、滑动效果的返回顶部代码
2014/03/18 Javascript
js和jquery如何获取图片真实的宽度和高度
2014/09/28 Javascript
浅谈Javascript Base64 加密解密
2014/12/28 Javascript
jQuery中toggleClass()方法用法实例
2015/01/05 Javascript
Javascript中的return作用及javascript return关键字用法详解
2015/11/05 Javascript
JavaScript统计字符串中每个字符出现次数完整实例
2016/01/28 Javascript
纯JavaScript手写图片轮播代码
2016/10/20 Javascript
微信小程序开发之入门实例教程篇
2017/03/07 Javascript
node.js平台下利用cookie实现记住密码登陆(Express+Ejs+Mysql)
2017/04/26 Javascript
AngularJS基于http请求实现下载php生成的excel文件功能示例
2018/01/23 Javascript
Vue+Element UI+Lumen实现通用表格分页功能
2019/02/02 Javascript
微信公众号生成新浪短网址的实现(快速生成)
2019/08/18 Javascript
[52:14]VG vs Serenity 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/20 DOTA
[01:11:02]Secret vs Newbee 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/17 DOTA
运行django项目指定IP和端口的方法
2018/05/14 Python
python用fsolve、leastsq对非线性方程组求解
2018/12/15 Python
Python学习笔记之Zip和Enumerate用法实例分析
2019/08/14 Python
python psutil监控进程实例
2019/12/17 Python
安装不同版本的tensorflow与models方法实现
2021/02/20 Python
全球度假村:Club Med
2017/11/27 全球购物
几个常见的消息中间件(MOM)
2014/01/08 面试题
《盲人摸象》教学反思
2014/02/16 职场文书
监察建议书范文
2014/03/12 职场文书
遗体告别仪式主持词
2014/03/20 职场文书
酒店优秀员工事迹材料
2014/06/02 职场文书
2015年为民办实事工作总结
2015/05/26 职场文书
预备党员半年考察意见
2015/06/01 职场文书
咖啡厅里的创业计划书
2019/08/21 职场文书
JS中一些高效的魔法运算符总结
2021/05/06 Javascript
MySQL Shell import_table数据导入的实现
2021/08/07 MySQL
向Spring IOC 容器动态注册bean实现方式
2022/07/15 Java/Android