等待时间
前端性能,个人理解为在用户输入URL或者点击链接之后,直到网页完全展现所需要的时间,本文暂时定义为等待时间。
那么前端性能优化就是要缩短这个等待时间。
定义问题
等待时间没有满足用户预期。
明确目标
缩短等待时间,达到用户预期或者技术预期。
分析问题
都知道有钱可以为所欲为,那么也可以买配置更高的电脑,速度更快的宽带,性能更好的服务器,接下来的讨论都受到贫穷的限制,无法负担得起昂贵的费用,这是一个前提(背景)。
从输入 URL 到页面加载完成,发生了什么?
分解问题
由流程可知,等待时间时间主要由网络请求,页面渲染这两大方面构成。
- 建立连接,传输请求,传输响应都归纳到网络请求;众所周知,网络一直是计算机系统的性能瓶颈之一。
- 页面渲染有很多性能浪费可以避免,需要足够重视。
- 发起请求,接收响应耗费时间很少,优化空间很小,可以忽略。
- 响应请求,构建响应是后端性能优化的重点,可以忽略。
寻找方式
明确目的,分解问题之后,现在是要缩短网络请求时间和页面渲染时间。
由物理可知,时间=总量/速率
,想要缩短时间,可以提高速率或者减少总量。
流程中,有些步骤是可以优化的,可以减少中间的某些步骤消耗的时间。
寻找方法
老许,你要性能不要,只要你开金口,我马上就给你想办法
不搞这些虚的了,首先从常用技术入手,由于常用技术总是变化(吐槽一下,前端技术更新比我的亚索都快),所以就找到他们的祖宗,HTML,CSS,JavaScript,其实改进他们就是具体方法,更具有可操作性。
小结
在贫穷的限制下,我们将性能优化分解成了网络请求和页面渲染两个方面,通过优化HTML,CSS,JavaScript的方法来达到目的
网络请求
HTTP遍布了互联网的每个角落,那么对HTTP的理解,使用,优化就很重要了。
网络请求分为HTTP和非HTTP两方面,而非HTTP又分为HTML,CSS,JavaScript。
HTTP优化
keep-alive连接,持久连接重用了TCP连接,减少了连接建立的开销,使连接保持在已谐调状态。详细参考《HTTP权威指南》 4.2.2 性能聚焦区域和4.5 持久连接
正确使用HEAD方法,可以减少传输不必要的响应主体
使用内容编码技术,可以减小资源的体积,一般使用gzip
使用范围请求首部,可以只请求资源的一部分;甚至可以并行发送范围请求,同时获取资源的不同部分,然后拼接回去
使用差异编码技术,可以只请求资源的变更部分,这一技术需要客户端和服务端支持
HTTP缓存
私有缓存一般由客户端提供给用户使用,共有代理缓存一般由公司提供给多个客户端使用。
缓存可以减少资源的传输,效果取决于命中率,一般有文档命中率,更精确的有字节命中率。
强制缓存
对比 | Expires | ?Cache-Control |
---|---|---|
用法 | Expires: <date> | Cache-Control: max-age=<s> |
说明 | date是GMT,在date之前可以使用缓存 | s是数字,单位是秒,在s秒内可以使用缓存 |
注意 | 使用绝对时间,时钟不同步会导致问题 | - |
优先级 | 低 | 高 |
协商缓存
标识为no-cache的响应实际上是可以缓存的,只是再验证成功之后才允许使用缓存;如果再校验失败,需要缓存返回的新资源
首部 | 版本 | 备注 |
---|---|---|
Pragma: no-cache | HTTP/1.0+ | 虽然不推荐使用,但是加上可以兼容HTTP/1.0+ |
?Cache-Control: no-cache | HTTP/1.1 |
首部 | 备注 |
---|---|
If-Modified-Since: <date> | 需要Last-Modified: <date>配合 |
If-None-Match: <tags> | 需要ETag: <tags>配合 |
对比 | Last-Modified | ETag |
---|---|---|
响应 | Last-Modified: <date> | ETag: <tags> |
请求 | If-Modified-Since: <date> | If-None-Match: <tags> |
说明 | date是GMT,验证资源在date之后有没有被修改 | tags是标识符,验证资源有没有被修改 |
程度 | 弱验证 | 可强可弱 |
注意 | 内容没有变化,但是date却变化了 内容变化了,但是不重要,date也变化了 date精确到秒,内容在亚秒级变化,date却没有变化 |
tags可以赋予多种含义,例如版本号,内容hash值等 |
强验证和弱验证
强验证和弱验证的区别是:能不能唯一标识资源的一个实例
ETag,本身是强验证码,也可以作为弱验证码来用,验证会匹配字符串全部
ETag,如果加了W/前缀,就是弱验证码,验证会匹配字符串部分,只要前缀匹配,无论后面的时候一样,都会通过验证
Last-Modified,就是弱验证码,由于时间只能精确到秒,而一秒内,文件可能变化了很多次,所以是弱验证码
E-tag: "3116092995",假设这个值是文件大小,那么即使没有W/前缀,它也是弱验证码
HTTP/2
请求/响应复用
服务端推送
首部压缩
参考内容
非HTTP优化
CDN
CDN(Content Delivery Network,即内容分发网络)是一种分布式缓存,不仅有缓存的优点,还有地理优势,它们通常会智能地选择与用户更近的设备提供资源。
cookie优化
同一个域名下的请求会不分青红皂白地携带 Cookie,而静态资源往往并不需要 Cookie 携带什么认证信息。把静态资源和主页面置于不同的域名下,完美地避免了不必要的 Cookie 的出现!
看起来是一个不起眼的小细节,但带来的效用却是惊人的。以电商网站静态资源的流量之庞大,如果没把这个多余的 Cookie 拿下来,不仅用户体验会大打折扣,每年因性能浪费带来的经济开销也将是一个非常恐怖的数字。
异步操作
异步可以不用等待磁盘IO,网络IO,更加有效地利用资源。
举例说明:A请求需要3s,B请求需要5s
同步需要8s,异步需要5s
同步:0s时A请求发送,3s时A请求完成,B请求发送,8s时B请求完成
异步:0s时A请求发送,B请求发送,3s时A请求完成,5s时B请求完成
同步会阻塞其他操作,异步不会阻塞其他操作,这里不再赘述。
压缩
HTML压缩,CSS压缩,JavaScript混淆,压缩,可以减小资源的体积。
这里的压缩是指删除注释,空格,回车等不必要的字符,和内容编码的压缩是两种方式。
CSS简化
CSS要尽量精简,尽量复用,不要定义过大的class
如果使用SCSS,过多使用mixin,function,会增加过多重复的CSS代码
举例说明,只是说明意思
<button class="bad-ok-button">OK</button>
<button class="bad-cancel-button">Cancel</button>
bad-ok-button {
border:2px solid; border-radius:25px;
background: green;
};
bad-cancel-button {
border:2px solid; border-radius:25px;
background: red;
};
<button class="good-button good-button--ok">OK</button>
<button class="good-button good-button--cancel">Cancel</button>
good-button { border:2px solid; border-radius:25px; };
good-button--ok { background: green; };
good-button--cancel { background: red; };
页面渲染
就拿Chrome来说,有Google这个爸爸,其实是不需要我这样的菜鸡去担忧,操心浏览器渲染的优化,其实我所能做的最大的努力也是一句话,就是不添乱。
标签位置
CSS资源放在head中,因为CSS下载完成才会构建CSSOM树,CSSOM树完成才会构建Render树。
JavaScript资源放在body底部,因为JavaScript会抢占控制权。
JavaScript引擎和Render引擎是相互独立的。
因为JavaScript可能会修改DOM和CSSOM,所以JavaScript执行时候,会停止渲染,因为那是徒劳的。
预处理
dns-prefetch提前进行域名解析
pre-connect提前进行TCP连接
preload,提高资源下载优先级,表明资源是现在必要的资源
prefetch,降低资源下载优先级,表明资源是之后需要的资源
参考内容
异步操作
不经过任何处理的情况下,下载和执行都会阻塞HTML解析
async异步下载(不阻塞),加载完成后马上执行(阻塞),不一定会按照顺序执行
defer异步下载(不阻塞),在所有元素解析完成之后,DOMContentLoaded
事件触发之前执行(虽然阻塞,但是首次HTML解析已经完成),会按照顺序执行。
参考内容
DOM操作
页面渲染,会经过flow和paint,而Repaint开销很大,Reflow开销更大,尽量减少触发Reflow和Repaint,或者减少开销
- 减少读取DOM元素的属性
- 将对DOM元素的多次操作合并
将DOM元素拷贝,进行多次操作后,替换原DOM元素
对DOM元素修改display: none
,进行多次操作后,恢复显示
对DOM元素的多次CSS属性修改,可以替换为对class修改 -
position
属性为absolute
或fixed
的元素,重排的开销会比较小,因为不用考虑它对其他元素的影响 - 用
transform
做形变和位移
CSS优化
CSS要尽量简单,不要过分嵌套,不要过分使用类选择器以外的选择器
简单的CSS可以减轻渲染的压力,也更加容易修改和维护
注意:CSS选择器是从右向左进行匹配的
参考内容
BEM,CSS模块化
渐进式图片
先下载低像素的图片,既节省时间,又减小首次渲染压力;后面会下载正常像素图片,提供完美的用户体验。
按需加载
图片的按需加载,不是首屏需要的图片,在需要的时候再加载
JavaScript优化
尽量访问局部变量,尽量减少作用域链搜索,可以加快JavaScript执行,从而更快渲染页面
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。