页面性能优化:preload预加载静态资源
本文主要介绍preload的使用,以及与prefetch的区别。然后会聊聊浏览器的加载优先级。
preload 提供了一种声明式的命令,让浏览器提前加载指定资源(加载后并不执行),在需要执行的时候再执行。提供的好处主要是
- 将加载和执行分离开,可不阻塞渲染和 document 的 onload 事件
- 提前加载指定资源,不再出现依赖的font字体隔了一段时间才刷出
如何使用 preload
使用 link 标签创建
<!-- 使用 link 标签静态标记需要预加载的资源 -->
<link rel="preload" href="/path/to/style.css" 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">
<link rel="preload" as="script" href="https://a.xxx.com/xxx/PcCommon.js">
<link rel="preload" as="script" href="https://a.xxx.com/xxx/TabsPc.js">
如何判断浏览器是否支持 preload
目前我们支持的浏览器主要为高版本 Chrome,所以可放心使用 preload 技术。 其他环境在 caniuse.com 上查到的支持情况如下:
在不支持 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 后,Chrome 会有一个警告:
如上文所言,若不确定资源是必定会加载的,则不要错误使用 preload,以免本末倒置,给页面带来更沉重的负担。
当然,可以在 PC 中使用 preload 来刷新资源的缓存,但在移动端则需要特别慎重,因为可能会浪费用户的带宽。
避免混用 preload 和 prefetch
preload 和 prefetch 混用的话,并不会复用资源,而是会重复加载。
<link rel="preload" href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff" as="font">
<link rel="prefetch" href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff" as="font">
使用 preload 和 prefetch 的逻辑可能不是写到一起,但一旦发生对同一资源 preload 或 prefetch 的话,会带来双倍的网络请求,这点通过 Chrome 控制台的网络面板就能甄别:
避免错用 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">
然而我发现这个文件加载了两次:
原因是对跨域的文件进行 preload 的时候,我们必须加上 crossorigin 属性:
<link rel="preload" as="font" crossorigin href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff">
再看一下网络请求,就变成一条了。
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.
那为何会有两条请求,且优先级不一致,又没有命中缓存呢?这就得引出下一个话题来解释了。
不同资源加载的优先级规则
我们先来看一张图:
这张表详见:Chrome Resource Priorities and Scheduling
这张图表示的是,在 Chrome 46 以后的版本中,不同的资源在浏览器渲染的不同阶段进行加载的优先级。 在这里,我们只需要关注 DevTools Priority 体现的优先级,一共分成五个级别:
- Highest 最高
- Hight 高
- Medium 中等
- Low 低
- Lowest 最低
html 主要资源,其优先级是最高的
css 样式资源,其优先级也是最高的
CSS(match) 指的是对已有的 DOM 具备规则的有效的样式文件。
script 脚本资源,优先级不一
前三个 js 文件是写死在 html 中的静态资源依赖,后三个 js 文件是根据首屏按需异步加载的组件资源依赖,这正验证了这个规则。font 字体资源,优先级不一
css 样式文件中有一个 @font-face 依赖一个 font 文件,样式文件中依赖的字体文件加载的优先级是 Highest; 在使用 preload 预加载这个 font 文件时,若不指定 crossorigin 属性(即使同源),则会采用匿名模式的 CORS 去加载,优先级是 High,看下图对比: 第一条 High 优先级也就是 preload 的请求:
第二条 Highest 也就是样式引入的请求:
可以看到,在 preload 的请求中,缺少了一个 origin 的请求头字段,表示这个请求是匿名的请求。 让这两个请求能共用缓存的话,目前的解法是给 preload 加上 crossorigin 属性,这样请求头会带上 origin, 且与样式引入的请求同源,从而做到命中缓存:
<link rel="preload" as="font" crossorigin href="https://at.alicdn.com/t/font_zck90zmlh7hf47vi.woff">
这么请求就只剩一个:
在网络瀑布流图中,也显示成功预加载且后续命中缓存不再二次加载:
总结
preload 是个好东西,能告诉浏览器提前加载当前页面必须的资源,将加载与解析执行分离开,做得好可以对首次渲染带来不小的提升,但要避免滥用,区分其与 prefetch 的关系,且需要知道 preload 不同资源时的网络优先级差异。
preload 加载页面必需的资源如 CDN 上的字体文件,与 prefetch 预测加载下一屏数据,兴许是个不错的组合。
页面性能优化:preload预加载静态资源的更多相关文章
- flask模板应用-加载静态文件:添加Favicon,使用CSS框架,使用宏加载静态资源
加载静态文件 一个Web项目不仅需要HTML模板,还需要许多静态文件,比如CSS.JavaScript文件.图片和声音声.在flask程序中,默认需要将静态文件存储在与主脚本(包含程序实例的脚本)同级 ...
- Flask之加载静态资源
Flask之加载静态资源 1.加载css样式 <link rel="stylesheet" href="{{ url_for('static',filename=' ...
- nginx反向代理部署springboot项目报404无法加载静态资源
问题:nginx反向代理部署springboot项目报404无法加载静态资源(css,js,jpg,png...) springboot默认启动端口为8080,如果需要通过域名(不加端口号)直接访问s ...
- Django的urls.py加载静态资源图片,TypeError: view must be a callable or a list/tuple in the case of include().
Django的urls.py加载静态资源图片,TypeError: view must be a callable or a list/tuple in the case of include(). ...
- Thymeleaf模板引擎绕过浏览器缓存加载静态资源js,css文件
浏览器会缓存相同文件名的css样式表或者javascript文件.这给我们调试带来了障碍,好多时候修改的代码不能在浏览器正确显示. 静态常见的加载代码如下: <link rel="st ...
- 关于springboot2.*版本无法加载静态资源
前言 在学习springboot的过程中,发现无法引用静态资源.我使用的是springboot2.2.1版本. 追溯源码,终于解决.并记录下解决思路. 默认加载路径 首先得知道springboot默认 ...
- SPA应用性能优化(懒加载)
前提: 如今开发方式都是采用前后台分离的方式,前台采用的方式则是单页面应用开发简称SPA,这种开发模式最大的一个特点就是将有所代码打包成了一个文件, 这会导致了一个问题就是如果这个应用过大,打出来的这 ...
- requirejs:性能优化-及早并行加载
为了提高页面的性能,通常情况下,我们希望资源尽可能地早地并行加载.这里有两个要点,首先是尽早,其次是并行. 通过data-main方式加载要尽可能地避免,因为它让requirejs.业务代码不必要地串 ...
- 前端性能优化(四)——网页加载更快的N种方式
网站前端的用户体验,决定了用户是否想要继续使用网站以及网站的其他功能,网站的用户体验佳,可留住更多的用户.除此之外,前端优化得好,还可以为企业节约成本.那么我们应该如何对我们前端的页面进行性能优化呢? ...
随机推荐
- VBA Do...While循环
一个Do...while循环用于只要条件为真就重复一组语句.该条件可以在循环开始时或循环结束时检查. 语法 以下是VBA中的一个Do...While循环的语法. Do While condition ...
- ES6-promise实现异步请求
一.Promise是什么 简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果. ES6规定,Promise对象是一个构造函数,用来生成Promise实例.Promise构 ...
- 虹软人脸识别 - Android Camera实时人脸追踪画框适配
在使用虹软人脸识别Android SDK的过程中 ,预览时一般都需要绘制人脸框,但是和PC平台相机应用不同,在Android平台相机进行应用开发还需要考虑前后置相机切换.设备横竖屏切换等情况,因此在人 ...
- awk 常用选项及数组的用法和模拟生产环境数据统计
awk 常用选项总结 在 awk 中使用外部的环境变量 (-v) awk -v num2="$num1" -v var1="$var" 'BEGIN{print ...
- Linux建立虚拟ip的方法
文章来源 运维公会:Linux建立虚拟ip的方法 1.虚拟ip的介绍 虚拟IP地址(VIP) 是一个不与特定计算机或一个计算机中的网络接口卡(NIC)相连的IP地址.数据包被发送到这个VIP地址, ...
- Django中过滤的实现
过滤模块 安装 >: pip install django-filter 注册应用:settings/dev.py INSTALLED_APPS = [ # 列表过滤模块 'django_fil ...
- Visual Studio 快捷键、常见问题
前言 由于平时自己在使用 VS 过程中遇到过一些很常见的问题,比如基本的设置.工具等.VS 十分的强大(宇宙第一 IDE),所以有些功能藏的深也不好找,就在这里记录下. 正文 一.快捷操作 1.调试. ...
- BZOJ 2277 strongbox (gcd)
题意 有一个密码箱,0到n-1中的某些整数是它的密码. 且满足,如果a和b都是它的密码,那么(a+b)%n也是它的密码(a,b可以相等) 某人试了k次密码,前k-1次都失败了,最后一次成功了. 问:该 ...
- Java知识点汇总-2
目录 1 变量的作用域 2 二维数组的定义 1 变量的作用域 实例代码: public void fight(String name){ if ("Bean".equals(nam ...
- Druid连接池 报错:abandon connection原因分析
问题现象:使用Druid的数据库连接池,在进行一个查询SQL的时候,抛出了异常: [2017-10-20 01:40:59.269 ERROR com.alibaba.druid.pool.Druid ...