# web性能实战
本书对web性能限定为网站的加载速度,主要措施是减少传输的数据量。
主要包括以下具体措施:
- 缩小资源
- 缩小css
- 缩小javascript
- 缩小HTML
- 服务器压缩
- 压缩图像
# 优化CSS
- 简写CSS,比如margin padding这样的属性尽可能把属性值合并起来
- 减少CSS选择器的层级,尤其是使用了less/sass的嵌套
- 同样的CSS属性在不同选择器下多次声明,可合并选择器
- 分割CSS,减少入口CSS体积
- 如果直接使用CSS框架,按需下载需要的样式
- 避免使用@import(会造成串行下载而不是并行下载)(然而就没见人用过)
- 将link放在head标签中(解决无样式内容闪烁,同时尽早加载样式)
- 使用CSS动画/过渡而不是js动画
- 首屏CSS内联(无需请求独立的样式文件 首屏渲染快)
- 使用preload非阻塞加载样式文件(css加载会阻塞DOM树的渲染 css加载会阻塞后面js语句的执行)
# 优化图片
图片优化的一个主要目标是针对不同屏幕采用不同大小的图片,既能保证视觉效果,又能节约传输图片体积。下面的基于媒体查询优化CSS的图片,srcset属性以及picture元素都是这一思路的体现。
采用svg可保证同一张图片可以适配不同屏幕。
# 基于媒体查询优化CSS中的图片
可以基于不同屏幕采用不同的图片:
#app{
background-image:url('img/sm.png');
}
@media (min-width:1024px){
#app{
background-image:url('img/medium.png');
}
}
针对高清屏的需要展示高清图片:
@media screen (-webkit-min-device-pixel-ratio:2),(min-resolution:192dpi){
#app{
background-image:url('img/large.png');
}
}
# 基于srcset选择不同图片
img标签的srcset属性相当于是加强版的src,它可以指定多张图片,并且指定图片宽度,浏览器会根据需求从srcset中选择一个最合适的下载:
<img
src="img/small.jpg"
srcset="img/small.jpg 512w,img/medium.jpg 768w,img/large.jpg 1280w"
>
上面指定了三张图片,宽度分别为512 768和1280,w表示宽度
还可以使用sizes属性进一步控制:
<img
src="img/small.jpg"
srcset="img/small.jpg 512w,img/medium.jpg 768w,img/large.jpg 1280w"
sizes="(min-width:704px) 50vw, (min-width:480px) 75vw, 100vw"
>
上面代码的含义是: 屏幕704px及以上 图片宽度50w,480-703px 图片宽度75w,其他图片宽度100w
# 使用picture元素选择不同图片
picture元素和srcset属性所解决的问题很相似,都是提供一组图供浏览器选择,srcset适合一组图片长宽比一致的,不一致的适合picture元素。
<picture>
<source media="(min-width:704px)" srcset="img/medium.jpg 384w" sizes="33.3vw">
<source srcset="img/small.jpg 320w" sizes="75vw">
<img src="img/small.jpg">
</picture>
当屏幕宽度为704px或者更宽,采用medium图片,图片宽度384px,图片以33.3w渲染;小于704,第二个source标签生效,图片宽度320px,以75w展示。img标签是用来进行回退的。
为了适配高清屏,还可以可以进一步配置source元素的srcset属性
<picture>
<source media="(min-width:704px)" srcset="img/medium.jpg 384w,img/large.jpg 512w" sizes="33.3vw">
<source srcset="img/small.jpg 1x,img/medium.jpg 2x" sizes="75vw">
<img src="img/small.jpg">
</picture>
当屏幕尺寸不小于704px时,高清屏可以采用宽度为512的图片。其他情况下,即第二个source属性,1x表示适合标准DPI屏幕,2x表示适合更高DPI屏幕。
# 雪碧图
雪碧图其实是HTTP1时代合并请求这一策略在图片上的体现,它可以减少请求次数,但是一个图更改意味着整个图片都要替换,不利于缓存,在HTTP2时代这个认为是反模式,不推荐使用
# 图片压缩
在保证视觉效果的前提下压缩图片
# 使用webP图片
# 图片懒加载
# 优化字体
本书中提到可以限制字体数量,取字符子集限制字体等措施。然而中文环境下用字体主要是来做icon,所以这些措施基本无效。
# 优化JavaScript
# 合理放置script元素
因为script标签会阻塞页面渲染,一般推荐把script标签放在body底部,而不是head中。不过现在都是SPA,都是脚本控制的渲染,这个建议不是很有用。然而考虑到有骨架屏这个概念,显示纯展示一个骨架屏,等应用代码加载好再控制页面,这时显然script应该放在底部。
# 使用script的async属性
该属性使得script在加载的时候不会阻塞渲染,同时脚本下载完成后不需要等待其他脚本下载完成,立即执行。
# 基于service-worker和cacaheStorage缓存资源
service-worker相当于是前端能控制的代理服务器,可以用来缓存资源。
const cacheVersion = 'v1';
// 要缓存的资源
const cacheAssets = [];
self.addEventListener("install",function(event){
// cacheStorage 缓存资源
event.waitUntil(caches.open(cacheVersion).then(function(cache){
return cache.addAll(cacheAssets)
}).then(function(){
// 跳过waiting阶段(因为可能有别的serviceworker在控制),当前service-wroker直接控制页面
return self.skipWaiting();
}));
});
self.addEventListener("fetch",function(event){
// 缓存优先,无缓存时访问网络并缓存
event.responseWith(
caches.open(cacheVersion).then(function(cache){
return cahce.match(event.request).then(function(cacheResponse){
return cacheResponse || fetch(event.request).then(function(fetchResponse){
cache.put(event.request,fetchResponse.clone())
return fetchResponse;
})
})
})
)
});
当我们更新版本时,需要把旧的缓存删除
self.addEventListener("activate",function(event){
const whiteList = ["v2"];
event.waitUntil(
caches.keys().then(function(keyList){
return Promise.all([
...keyList.map(function(key){
if(!whiteList.includes(key)){
return cahces.delete(key)
}
return Promise.resolve();
}),
self.clients.claim(),
])
})
)
});
# 使用资源提示
# dns-prefetch preconnect
<link rel="preconnect" href="https://github.com/jiangshanmeta">
<link rel="dns-prefetch" href="https://github.com/jiangshanmeta">
这两个是针对跨域资源的优化,preconnect会建立连接 但是这个属性兼容性比较差,dns-prefetch只做dns解析,不建立连接,但是兼容性相对好点。
# prefetch preload
<link rel="preload" href="/fonts/font.woff" as="font">
<link rel="prefetch" href="/uploads/images/pic.png">
preload用来指定页面加载后很快会被用到的资源,prefetch用来告诉浏览器在页面加载完成后,利用空闲时间提前获取用户未来可能会访问的内容。
# HTTP2优化
HTTP1有以下问题:
- 队首阻塞(HTTP1 可以通过管道化进行优化,管道化的意思是不必和以前一样在响应完成后再发起下一个请求,而是可以并行地发起多个请求,但是响应必须按照请求顺序进行,因此一旦一个响应需要的时间比较长就会阻塞后面的响应)
- 未压缩头部
HTTP2采用了新的传输机制,不同请求的响应可以不按顺序返回,因而解决了队首阻塞问题。对于HTTP头,采用HPACK压缩算法,增量传输头部字段,减少了传数量。
另外HTTP2还可以把后面很快就要用到的资源推给客户端。