最近的项目中为了可以提高那么一丢丢性能,尝试了一下对 chunks
进行预加载处理。虽然作了异步加载的处理,可是项目大小决定了仍是有多个异步的 chunk.js
须要进行预加载,这里我指的是 preload
与
prefetch
。css
这里推荐一个 GoogleChromeLabs
团队推出的插件:
preload-webpack-plugin
。html
A webpack plugin for injecting <link rel='preload|prefecth'> into HtmlWebpackPlugin pages, with async chunk support
使用大体以下,具体参考其文档:前端
config = rewirePreloadPlugin(config, env, {
rel: 'preload',
include: 'initial',
as(entry) {
if (/\.(css|less)$/.test(entry)) return 'style';
if (/\.woff$/.test(entry)) return 'font';
if (/\.(png|jpg|jpeg|svg)$/.test(entry)) return 'image';
return 'script';
}});
复制代码
这个插件对字体元素( woff
),自动加了跨域:react
源码:webpack
const crossOrigin = asValue === 'font' ? 'crossorigin="crossorigin" ' : '';
filesToInclude+= `<link rel="${options.rel}" as="${asValue}" ${crossOrigin}href="${entry}">\n`复制代码
使用后:git
将会在你的 index.html
中的 Head
标签里面添加一堆 <link rel='preload' as='script' href='xxx.chunk.js'>
github
这就意味着这些资源都会被预加载,而后从缓存中去获取。将会提高网页打开的效率,可是滥用也会浪费带宽。web
实际使用中有一个问题,就是我使用的是 create-react-app
而且没有 eject
的状况下,目前这个插件对于 preload
的模式包括 include: 'initial' | 'allChunks' | 'asyncChunks' |'allAssets' | ['chunkName']
,(ps: initial
模式仍是尤雨溪大大提的 PR )而且提供了黑白名单,应该说是很细粒度的控制咱们去按需设置文件为 preload
的。chrome
可是实际上仍是会形成有我不须要其 preload
的文件被设置了,这就形成了浪费。express
<link rel='prefetch'>
出现的更早,浏览器支持度也挺不错,通俗的理解是着眼于下一个页面资源的预加载,因此在当前页面的优先级是很低的。
prefetch
在浏览器空闲时间下载或预取用户在不久的未来可能访问的文档,在当前的页面加载完成后预取资源放进缓存中,在以后的调用就直接从缓存中获取,从而提高性能。
<link rel='preload' as=''>
则是着眼于如今(当前页面)。浏览器遇到 rel= 'preload' 的标签就会将其推入到预加载器中,这个预加载器也将用于其余咱们所须要的,各类各样的,任意类型的资源。为了完成基本的配置,你还须要经过 href
和as
属性指定须要被预加载资源的资源路径及其类型。
引用一段 MDN 的描述:
<link>
元素的rel
属性的属性值preload
可以让你在你的HTML页面中<head>
元素内部书写一些声明式的资源获取请求,能够指明哪些资源是在页面加载完成后即刻须要的。对于这种即刻须要的资源,你可能但愿在页面加载的生命周期的早期阶段就开始获取,在浏览器的主渲染机制介入前就进行预加载。这一机制使得资源能够更早的获得加载并可用,且更不易阻塞页面的初步渲染,进而提高性能。本文提供了一个如何有效使用preload
机制的基本说明
其特色是 声明式的资源获取请求(fetch)、不易(注意不是不会)阻塞 onLoad、as 提供的细粒度的控制
总结其优点:
Accept
请求头。注意:忽略 as 属性,或者错误的 as 属性会使 preload 等同于 XHR 请求,浏览器不知道加载的是什么,所以会赋予此类资源很是低的加载优先级。
preload
还支持 onload
预加载完成回调、MIME、跨域获取、响应式的预加载、脚本化加载等,更多能够参考 MDN,以及这里,还有这里。
既然 preload
以及 prefetch
都是优先从 HTTP
缓存中获取资源,咱们必然要接触不少 304 Not Modified
响应。
咱们先来看一个 304 响应:
304 中最重要的两个请求头就是 If-None-Match
和 If-Modified-Since
。
前者的值是上一次响应中返回的 ETag
,ETag
是对该资源的一种惟一标识,只要资源有变化,Etag
就会从新生成。服务器接收到请求头中的 If-None-Match
以后就和文件资源的 Etag
作比较,若是同样,说明资源没有变化,就返回一个 304 Not Modified
而且没有响应体。客户端收到 304 响应后,就会从缓存中读取对应的资源,若是不相同,则表示资源发生了改变,那么服务器就会返回 HTTP/200 OK
响应,响应体就是该资源当前最新的内容.客户端收到 200 响应后,就会用新的响应体覆盖掉旧的缓存资源。
后者对应的上一次响应返回的 Last-Modified
。Last-Modified
是该资源文件最后一次更改时间,服务器会在 response header
里返回,同时浏览器会将这个值保存起来,在下一次发送请求时,放到 request header
里的 If-Modified-Since
里,服务器在接收到后也会作比对,若是没过时则返回304,若是过时则返回200 ok。一样会更新旧的文件。
若是两个头部都不带的请求就是无条件(unconditionally)请求该资源,服务器也就必须返回完整的资源数据。
上面所述的几个头部就是协商缓存的关键人物了。
这里还要说明一个概念就是条件请求,所谓条件就是没法肯定前端缓存资源是否最新,因此经过上述的头部来作一个验证,返回 304 或者 200。经过条件请求咱们能够节省出一个响应体,可是请求仍是发出去了。
这里若是连请求都不想发出去怎么办呢?
浏览器缓存主要有两类:缓存协商和完全缓存,也有称之为协商缓存和强缓存。
1.强缓存:不会向服务器发送请求,直接从缓存中读取资源,在chrome控制台的network选项中能够看到该请求返回200的状态码;
2.协商缓存:向服务器发送请求,服务器会根据这个请求的request header的一些参数来判断是否命中协商缓存,若是命中,则返回304状态码并带上新的response header通知浏览器从缓存中读取资源;
二者的共同点是,都是从客户端缓存中读取资源;区别是强缓存不会发请求,协商缓存会发请求。
浏览器缓存流程图:
因此咱们能够经过设置 Expires( HTTP1.0 ) 以及 Cache-Control( HTTP1.1 ),来命中强缓存,从而跳过发送请求的过程。
Expires:response header里的过时时间,浏览器再次加载资源时,若是在这个过时时间内,则命中强缓存。
Cache-Control:当值设为max-age= 600 时,则表明在这个请求正确返回时间(浏览器也会记录下来)的 10 分钟内再次加载资源,就会命中强缓存。
用户行为对浏览器缓存的控制:
未完待续....