前端性能优化
一、一次完整页面请求所发生的的事情
过程:
1.url解析
- 地址解析和编码
- HSTS
- 缓存解析
2.DNS域名解析
- 先进行本地DNS服务器解析,递归解析:
- 如果本地解析不到,再去域名服务器解析,迭代解析:
3.TCP三次握手建立连接:
4. 发送HTTP请求,服务器处理请求,返回响应结果
5.TCP四次挥手断开连接
5.浏览器渲染
二、前端性能优化
前端性能优化主要从上述过程中的关键节点进行优化,这也叫CRP。
1.缓存优化
客户端在向服务端发起请求之前要先检查是否有缓存,如果有缓存的话,直接将缓存渲染到页面,如果没有的话再发起请求。
(1)缓存位置
- Service Worker:浏览器独立线程进行缓存
- Memory Cache : 内存缓存
- Disk Cache:硬盘缓存
- Push Cache:推送缓存(HTTP/2中的)
一般情况下文存储在浏览器缓存中,数据存储在本地。
- 打开网页,地址栏输入地址: 查找 disk cache 中是否有匹配,如有则使用,如没有则发送网络请求。
- 普通刷新 (F5):因为 TAB 并没有关闭,因此 memory cache 是可用的,会被优先使用(如果匹配的话),其次才是 disk
cache。 - 强制刷新 (Ctrl + F5):浏览器不使用缓存,因此发送的请求头部均带有 Cache-control:
no-cache(为了兼容,还带了 Pragma: no-cache),服务器直接返回 200 和最新内容。
(2)硬盘与内存
- 硬盘读取速度慢,存储内容多;
- 内存读取速度快,存储内容少。
(3)浏览器缓存
浏览器缓存分为强缓存和协商缓存
强缓存在服务器中配置,协商缓存自己配置
强缓存 Expires / Cache-Control
浏览器对于强缓存的处理:根据第一次请求资源时返回的响应头来确定的
- Expires:缓存过期时间,用来指定资源到期的时间(HTTP/1)
- Cache-Control:cache-control:max-age=2592000第一次拿到资源后的2592000秒内(30天),再次发送请求,读取缓存中的信息(HTTP/1.1)
- 两者同时存在的话,Cache-Control优先级高于Expires
协商缓存 Last-Modified / ETag
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程
协商缓存生效,返回304和Not Modified
协商缓存失效,返回200和请求结果
Last-Modified和If-Modified-Since
- 第一次访问资源,服务器返回资源的同时,响应头中设置 Last-Modified(服务器上的最后修改时间),浏览器接收后,缓存文件和响应头;
- 下一次请求这个资源,浏览器检测到有
Last-Modified,于是添加If-Modified-Since请求头,值就是Last-Modified中的值; - 服务器再次收到这个资源请求,会根据 If-Modified-Since
中的值与服务器中这个资源的最后修改时间对比,如果没有变化,返回304和空的响应体,直接从缓存读取,如果If-Modified-Since的时间小于服务器中这个资源的最后修改时间,说明文件有更新,于是返回新的资源文件和200; - 但是Last-Modified 只能以秒计时,如果在不可感知的时间内修改完成文件,那么服务端会认为资源还是命中了,不会返回正确的资源;
ETag和If-None-Match
- Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),只要资源有变化,Etag就会重新生成;下一次加载资源向服务器发送请求时,会将上一次返回的Etag值放到请求头If-None-Match里,服务器只需要比较客户端传来的If-None-Match跟自己服务器上该资源的ETag是否一致,就能很好地判断资源相对客户端而言是否被修改过了。如果服务器发现ETag匹配不上,那么直接以常规GET 200回包形式将新的资源(当然也包括了新的ETag)发给客户端;如果ETag是一致的,则直接返回304知会客户端直接使用本地缓存即可。
2.DNS方面的优化
每一次DNS解析时间预计在20~120毫秒
减少DNS请求
即减少要请求的服务器,这个很难实现,因为目前我们都在将资源放在不同的服务器上,然后客户端从不同服务器上拉取资源。
如上图,我们利用反向代理服务器,将用户请求转发的服务器集群,这样做有这样几个好处:
- 网站安全的作用,来自互联网的访问请求必须经过代理服务器,相当于web服务器和可能的网络攻击之间建立了一个屏障。
- 可以通过配置缓存功能加速web请求。当用户第一次访问静态内容的时候,静态内容就被缓存在反向代理服务器上,这样当其他用户访问该静态内容的时候,就可以直接从反向代理服务器返回,加速web请求响应速度,减轻web服务器负载压力。事实上,有些网站会把动态内容也缓存在代理服务器上,比如*及某些博客论坛网站,把热门词条、帖子、博客缓存在反向代理服务器上加速用户访问速度,当这些动态内容有变化时,通过内部通知机制通知反向代理缓存失效,反向代理会重新加载最新的动态内容再次缓存起来。
- 此外,反向代理也可以实现负载均衡的功能,而通过负载均衡构建的应用集群可以提高系统总体处理能力,进而改善网站高并发情况下的性能。
(2)DNS预解析(DNS Prefetch)
link发送的是异步请求,因此在解析HTML代码的同时,就可以进行异步DNS解析,当用到后面的域名时,就已经解析好了,可以直接用,而不用等待。
<meta http-equiv="x-dns-prefetch-control" content="on">
<link rel="dns-prefetch" href="//static.360buyimg.com"/>
<link rel="dns-prefetch" href="//misc.360buyimg.com"/>
<link rel="dns-prefetch" href="//img10.360buyimg.com"/>
<link rel="dns-prefetch" href="//d.3.cn"/>
<link rel="dns-prefetch" href="//d.jd.com"/>
3.HTTP时的优化
减少HTTP请求次数和请求资源大小
- 资源合并压缩
- 字体图标
- Base64
- GZIP(一般的文件能压缩60%多)
- 图片懒加载
- 数据延迟分批加载
- CDN资源
CDN(content distribute network,内容分发网络)的本质仍然是一个缓存,而且将数据缓存在离用户最近的地方,使用户以最快速度获取数据,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度。即所谓网络访问第一跳,反向代理,如下图:
4.浏览器渲染优化
(1)浏览器渲染过程
构建DOM树、CSSOM树、渲染树
1> DOM树
字符编码—>字符集—>令牌—>Nodes节点—>DOM
2> CSSOM树
3>Render Tree 渲染树
总结步骤:
- 处理 HTML 标记,构建 DOM 树
- 处理 CSS 标记,构建 CSSOM 树
- 将 DOM 树和 CSSOM 树融合成渲染树
- 根据生成的渲染树,计算它们在设备视口(viewport)内的确切位置和大小,这个计算的阶段就是回流 => 布局(Layout)或重排(reflow)
- 根据渲染树以及回流得到的几何信息,得到节点的绝对像素 => 绘制(painting)或栅格化(rasterizing)
(2)优化方案
生成DOM树时
- 标签语义化,遵循W3C标准
- HTML标签避免多级嵌套
生成CSSOM树时
- CSS选择器避免深层次嵌套,多级层级从右到左渲染
.box a{}
.a{}
<div class="box">
<a href="">a<a/>
</div>
上面代码 .a{}比.box a{}快
- CSS预编译器,层级嵌套要慎用
- 减少使用@import阻塞渲染的请求,link写在头部(尽早尽快地把CSS下载到客户端(充分利用HTTP多请求并发机制)),样式少时可使用style.
名称 | 描述 |
---|---|
link | 异步加载,发送一个HTTP请求,单独线程取处理;同时渲染DOM |
@import | 同步加载,若使用了@import,则要等资源全部拉回来,才能继续渲染DOM |
style | 在html拿回来之前就已经发送请求,因此DOM渲染过程中不发送请求,因此若样式不多,使用style最快 |
避免阻塞的JS加载
- JS放在文件底部,因为script是同步加载,要先解析script里面的代码,再回来加载HTML
- defer和async,尽量使用defer
名称 | 描述 |
---|---|
defer | 发请求拉东西时,不阻塞渲染,拿回来之后,要等页面渲染完成之后,再加载JS,可以有依赖(例如Jquery) |
async | 发请求拉东西时,不阻塞渲染,拿回来之后,要先加载js及依赖,加载完成之后再继续渲染 |
生成render Tree时
减少回流和重绘
回流:元素大小和位置改变
重绘:元素样式的改变
每次页面渲染,必然有一次回流与重绘;
回流一定重绘,重绘不一定回流;
上一篇: [kmp+模板] kmp模板
下一篇: KMP算法