Web性能优化意义
1. 减少整体加载时间:减小文件体积、减少HTTP请求、使用预加载。
2. 使网站尽快可用:仅加载首屏内容,其他内容根据需要进行懒加载。
3. 平滑和交互性:使用CSS替代JS动画、减少UI重绘。
4. 加载表现形式:使用加载动画、进度条、骨架屏等过渡信息,让用户感觉到页面加载更快。
5. 性能监测:性能指标、性能测试、性能监控持续优化等
Web性能指标
- RAIL性能模型
- Response (响应):快速响应用户,在100ms内响应用户输入。
- Animation (动画):页面每一帧应该以16ms进行渲染,才能保持视觉上的平滑,并且避免卡顿。
- Idle (空闲):使用Javascript主线程时,应把任务的执行时间减少到50ms,这样可以释放线程用来进行交互。
- Load (加载):应在小于1s的时间内加载完成页面,并且可以进行交互。
- 基于用户体验的性能指标
-
First Contentful Paint (FCP):首次内容绘制
定义:浏览器首次绘制来自DOM的内容的时间。
包括内容:文本、图片、非白色的canvas或svg、Web字体文件。Tip:有内容并不意味着是有用的内容。
优化:FCP
-
Largest Contentful Paint (LCP):最大内容绘制
定义:可视区域内最大的内容元素呈现到屏幕上的时间,用以估算页面的主要内容对用户可见时间。考虑元素类型:
- < img >元素
- < svg >元素内的< image >元素
- < video >元素
- 使用url()函数加载背景图片的元素
- 包含文本节点或其他内嵌文本元素子元素的块级元素
优化:LCP
-
First Input Delay (FID):首次输入延迟
定义:从用户第一次与页面交互到浏览器实际能够响应该交互的时间。输入延迟是因为浏览器主线程忙于其他事情,不能响应用户。通常发生在首次内容绘制和可持续交互时间之间,因为页面已经呈现了一些内容,但是还不能可靠的交互。优化:参考LCP和FCP优化链接
-
Time to Interactive (TTI):完全可交互状态
定义:浏览器可以持续性的响应用户的输入。完全可交互状态的时间点在最后一个长任务(Long Task)完成的时间,并且在随后的几秒内网络和主线程是空闲的。优化:参考LCP和FCP优化链接
-
Total Block Time (TBT):总阻塞时间
定义:度量了FCP和TTI之间的总时间,在该事件范围内,主线程被阻塞足够长的时间以防止输入响应。优化:参考LCP和FCP优化链接
-
Cumulative Layout Shift (CLS):累计布局偏移
定义:测量在页面整个生命周期中发生的每个意外布局位移的所有单独布局位移分数的总和,他是一种保证页面的视觉稳定性从而提升用户体验的指标方案。优化:参考LCP和FCP优化链接
-
页面的生成过程
- 浏览器接收URL,浏览器查看缓存。如果缓存命中直接将内容提供个客户端,如果资源未命中则发起新请求。
- 浏览器解析URL获取协议,主机,端口,路径,组装成HTTP请求报文。
- 通过DNS域名解析系统获取IP地址。查找方式:浏览器缓存 -> 本地缓存 -> 路由器缓存 -> ISP DNS缓存 -> DNS迭代查询。
- 获取IP地址后进行TCP连接
- 第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认。
- 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态。
- 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
- 浏览器向服务器发送HTTP请求,并接受响应数据
- 页面渲染 — 关键渲染路径
- 解析HTML文档,构建DOM树、CSSOM树
- 执行Javascript脚本
- 根据DOM树和CSSOM树构建渲染树
- 根据渲染树确定所有节点的几何属性布局,然后绘制
- JS解析
- 词法分析
- 预编译
- 执行
- 回流与重绘
请求和响应优化
- 考虑方向
- 减少请求数量
- 缓存策略
- 减少获取资源的大小
- 常用方法
- 减少DNS查找
- 重用TCP连接:例如TCP持续链接,减少三次握手次数
- 减少HTTP重定向:HTTP重定向需要额外的DNS查询、TCP握手等十分耗时
- 压缩传输资源:如Gzip、图片压缩等
- 使用缓存:如CDN缓存、HTTP缓存等
- 使用CDN(内容分发网络)
- 客户端缓存资源
- 并行处理请求和响应
- 采用服务端渲染:解决首屏渲染慢的问题
- 预渲染加载静态页面
DNS优化
- 减少DNS的查找次数:当客户端中的DNS缓存为空时(浏览器和操作系统都为空),DNS查找的次数和页面中主机名的数量相同。减少主机名的数量可以减少DNS查找次数。
- 进行DNS预获取:DNS Prefetch是尝试在请求资源之前解析域名,这可能是后面要加载的文件,也可能是用户尝试打开的链接目标。HTML的link元素通过dns-prefetch的rel属性值提供此功能。
注意事项:- 每当站点引用跨域资源的时候,都应在head元素中放置DNS Prefetch提示。
- DNS Prefetch仅对跨域的DNS查找有效
- 谨慎使用,多页面重复DNS预解析会增加DNS查询次数
- 默认情况下浏览器会对页面中和当前域名不在同一个域的域名进行预获取,并且缓存结果,这就是隐式的DNS Prefetch。如果想对页面中没有出现的域进行获取,那么就要使用显示的DNS Prefetch。
- 虽然使用DNS Prefetch能够加快页面的解析速度,但是也不能滥用。
- 使用CDN加速域名
- 延长DNS缓存时间
HTTP长连接
- 长连接:HTTP 发送请求时,要先创建一个 TCP 连接,并在 TCP 连接上把 HTTP 请求的内容发送并且接收完返回,这是一次请求完成,浏览器与服务器进行协商是否关闭 TCP 链接,如果还有请求可以直接在这个 TCP 连接上发送,不需要经过创建时三次握手的消耗。可以通过Connection: keep-alive字段进行指定。(HTTP/1.1 版本)
- 短连接:浏览器向服务器每进行一次HTTP操作都要建立一个新的连接。(HTTP/1.0 版本)
长连接缺点:同一个TCP连接里,所有数据通行是按次序进行的。如果前面的响应慢会导致后面的请求排队。这被称为“队头阻塞”。
解决方法:
- 减少请求数
- 同时多开持久连接
压缩传输数据资源
- HTTP响应数据压缩
- 使用Gzip压缩文本:浏览器和服务器之间会使用主动协商机制,浏览器向服务器发送请求的时候,其请求标头中会含有Accept-Encoding字段,其中包含了客户端支持的压缩算法,服务器启用压缩,使用压缩算法对响应的消息主体进行压缩,并且发送Content-Encoding首部字段来告知浏览器它选择了哪种压缩算法。
- 压缩图片
- HTTP请求数据压缩
- 头部数据压缩:HTTP协议是不支持头信息压缩的,但是HTTP/2引入了头信息压缩机制,一方面,头信息可以使用gzip或compress压缩后再发送,另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号。
- 请求体数据压缩:压缩方式包含GZIP、ZLIB、DEFLATE。
HTTP缓存
- 强制缓存:当命中强缓存的时候,客户端不会再请求服务器,直接从缓存中读取内容。
强制缓存字段- Expires:为服务器返回的过期时间,浏览器再次加载资源时,如果在这个过期时间内,则命中强缓存。
- Cache-Control:
- no-store:禁用缓存。
- no-cache:不使用强缓存,直接进入协商缓存,即每次请求都必须向服务器发送。服务器接收到 请求,然后判断资源是否变更,是则返回新内容,否则返回304,未变更。
- max-age:缓存存储的最大时间。
- private:响应只能被客户端缓存。
- public:响应可以被任何对象(客户端,代理服务器等)缓存.
- 协商缓存:向服务器发送请求,服务器会根据这个请求的request header的一些参数来判断是否命中协商缓存,如果命中,则返回304状态码并带上新的response header通知浏览器从缓存中读取资源。
协商缓存字段- Etag:每次访问服务器都会返回一个新的token,第二次请求时,该值埋在请求头里的If-None-Match发送给服务器,服务器在比较新旧的token是否一致,一致则返回304通知浏览器使用本地缓存,不一致则返回新的资源,新的ETag,状态码为200。
- If-None-Match:当资源过期时,浏览器发现响应头里有Etag,则再次像服务器请求时带上请求头if-none-match(值是Etag的值)。服务器收到请求进行比对,决定返回200或304。
- Last-Modifed:服务器向浏览器发送最后的修改时间
- If-Modified-Since:当资源过期时,发现响应头具有Last-Modified声明,则再次向服务器请求时带上头if-modified-since,表示请求时间。服务器收到请求后发现有if-modified-since则与被请求资源的最后修改时间进行对比(Last-Modified),若最后修改时间较大,说明资源更改,则响应200,返回新资源;若最后修改时间较小,说明资源无新修改,响应304,使用缓存。
- 缓存决策及注意事项
- 示例
- HTML文件:保证其内容发生修改时能及时更新,应将其设置为协商缓存。
- 图片文件:考虑图片的数量和大小对客户端缓存空间有很大开销,以及对图片的修改基本是更换,所以可采用强制缓存并且国企时间不宜过长。
- 样式文件:属于文本文件但是内容不定期修改,所以通过增加文件指纹(style.24fd12f9.css),使用强制缓存提高效率。当文件修改后,不同文件会有不同指纹,则对资源重新请求。
- Javascript文件:采用文件指纹和较长的过期时间。
- 注意事项
- 拆分源码,分包加载:将不常修改的模块和常修改的模块分开打包。
- 预估资源的缓存时效:根据不同资源,规划相应的缓存更新时效。
- 控制中间代理的缓存:涉及用户隐私的避免使用中间代理缓存,对所有用户开放的资源考虑使用中间代理缓存。
- 避免网址冗余:不要将相同的资源设置为不同的URL。
CDN缓存
简介:CDN是将源站内容分发至全国所有的节点,从而缩短用户查看对象的延迟,提高用户访问网站的响应速度与网站的可用性的技术。
关键渲染路径优化
- 优化DOM
- 缩小HTML文件尺寸:删除注释、空格、换行等无用字符
- 使用gzip压缩
- 使用缓存
- 优化CSSOM
- 将任何非必须的CSS标记为非关键资源
- 减少关键CSS的数量
- 缩短CSS文件传送时间
- Javascript优化
- 缩小文件尺寸、gzip压缩、使用缓存
- 异步加载
- 避免同步请求
- 延迟解析
- 避免运行时间长的Javascript
- 使用web worker线程执行纯计算任务
- 函数防抖和节流