-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
前端性能优化知识集合 #108
Comments
Closed
666 |
1 similar comment
666 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
本文思维导图
页面请求过程
参考资料:https://developers.google.com/web/tools/chrome-devtools/network-performance/understanding-resource-timing?hl=zh-cn
探测页面性能状态
pagespeed 插件:
https://chrome.google.com/webstore/detail/pagespeed-insights-with-p/lanlbpjbalfkflkhegagflkgcfklnbnh?hl=zh-CN
每个阶段的优化点
prepare for unload
跳过
redirect
app cache
Cache-Control
||Expires
Last-Modified
||Etag
If-Modified-Since
If-None-Match
Queuing
排队的时间花费。如果某个请求正在排队,则指示:
优化方法
减少请求数量
HTTP/1.1 协议下,如 chrome 同域名资源最多 6 个,避免在单个网域检索太多的资源。
该方案对 HTTP/2 无效,因为 HTTP/2 可以单个 TCP 连接多路复用。
提高资源优先级
Stalled/Blocking
优化方案同 Queuing
DNS Lookup
pre-fetch
Request Sent / Sending
Waiting (TTFB)
首字节返回时间(TTFB)。
该环节耗时较长的主要原因:
客户端与服务器之间的网络条件较差
要解决长 TTFB,首先请尽可能缩减网络。理想的情况是将应用托管在本地,然后查看 TTFB 是否仍然很长。
服务器应用的响应慢
需要优化应用的响应速度。可以是优化数据库查询、为特定部分的内容实现缓存,或者修改您的网络服务器配置。
很多原因都可能导致后端缓慢。
如果本地托管后 TTFB 仍然漫长,那么问题出在您的客户端与服务器之间的网络上。很多事情都可以阻止网络遍历。客户端与服务器之间有许多点,每个点都有其自己的连接限制并可能引发问题。测试时间是否缩短的最简单方法是将您的应用置于其他主机上,并查看 TTFB 是否有所改善。
优化
Content Download / Downloading
如果该阶段耗时较长。
减少发送的字节数
使用 webp
webp 是一种支持有损压缩和无损压缩的图片文件格式,派生自图像编码格式 VP8。根据 Google 官方的数据,无损压缩后的 webp 比 PNG 文件少了 26% 的文件大小,有损压缩在具有同等 SSIM 索引的情况下 webp 比 jpeg 文件少 25~34% 的文件大小。webp 支持无损透明度(也叫作 alpha 通道),图片大小增加 22%。支持动画格式 Animated WebP。
图片质量
根据网络的不同,可以显示不同质量的商品图片。
图片锐化
不必要的浪费
合理地使用 CDN 图片尺寸可以带来下载图片的性能提升,还可以减少不必要的内存消耗。举例:
自然尺寸:
110\*110
显示尺寸:
100\*100
不必要的像素:
110\*110 - 100\*100 = 2100
不必要的内存消耗:
2100\*4/1024=8.2kb
2X:
\*4=32.8kb
3X:
\*9=73.8kb
使用 iconfont
iconfont 对于前端来说有很多优点:自由变化大小、矢量不失真、自由修改颜色、可以添加一些视觉效果如阴影、旋转、透明度、兼容 IE6.
为了节省流量,在移动端,目前只引用 ttf 一个字体文件,可以考虑 base64 在 css 文件中。
雪碧图注意
解码内存消耗
内存消耗公式:w * h * 4(宽 * 高 * 每个像素 4 个字节)
如果设备 DPI 大于 1,还需要乘以 DIP 系数,如 Retina 设备 X4,RetinaHD 设备 X8
禁止生成大图且利用率少
range request/response
Processing
页面的处理过程:https://developers.google.com/web/fundamentals/performance/critical-rendering-path/?hl=zh-cn
页面的构建过程。
HTML -- Nodes
一一对应
Nodes -- LayoutObjects
一一对应
LayoutObjects 到 PaintLayers
满足以下条件的 LayoutObjects 会拥有独立的渲染层,而其他的 LayoutObject 则和其第一个拥有渲染层的父元素共用一个。
PaintLayers 到 GraphicLayers(合成层)
渲染层提升为合成层有一个先决条件,该渲染层必须是 SelfPainingLayer,同时满足以下条件的会被提升为合成层:
直接原因
后代原因
overlap 重叠原因
层压缩
由于重叠的原因,可能随便就能产生大量的合成层,而每个合成层要消耗 CPU 和内存。浏览器考虑到了,于是有了层压缩。但不是万能的,有很多情况下,浏览器是无法进行层压缩的。这些也是我们应该避免的。
提升为合成层有以下好处:
合成层的位图,会交由 GPU 合成,比 CPU 处理要快。
当需要 repaint 时,只需要 repaint 本身,不会影响到其他层。
对于 transform 和 opacity 效果,不会触发 layout 和 paint。
对于 fixed 的合成层,移动时不会触发 repaint。
js / css 加载和执行
参考资料:
需要关注四个因素:DOM / CSSOM/ JS / 渲染
资源并行加载、串行执行
关于加载顺序的基本理解
CSS
JS
JavaScript 可以查询和修改 DOM 与 CSSOM。
JavaScript 执行会阻止 CSSOM。
除非将 JavaScript 显式声明为异步,否则它会阻止构建 DOM。
无论我们使用
<script>
标记还是内联 JavaScript 代码段,您都可以期待两者能够以相同方式工作。 在两种情况下,浏览器都会先暂停并执行脚本,然后才会处理剩余文档。不过,如果是外部 JavaScript 文件,浏览器必须停下来,等待从磁盘、缓存或远程服务器获取脚本,这就可能给关键渲染路径增加数十至数千毫秒的延迟。默认情况下,所有 JavaScript 都会阻止解析器。由于浏览器不了解脚本计划在页面上执行什么操作,它会作最坏的假设并阻止解析器。向浏览器传递脚本不需要在引用位置执行的信号既可以让浏览器继续构建 DOM,也能够让脚本在就绪后执行;例如,在从缓存或远程服务器获取文件后执行。
为此,我们可以将脚本标记为_异步_:
async
。如果浏览器尚未完成 CSSOM 的下载和构建,而我们却想在此时运行脚本,会怎样?答案很简单,对性能不利:浏览器将延迟脚本执行和 DOM 构建,直至其完成 CSSOM 的下载和构建。
动态加载的 js 的执行是不会受到 htm l后面外联的 js 的阻塞的影响,即是说,它的执行和后面 js 的执行顺序是不确定的
domContentLoaded
事件除了包含 DOM,还包括 script 标签,且包括 script 的执行时间cpu 负责 Layout,gpu 负责 Paint
60 fps 和 设备刷新率
如今大部分设备的屏幕刷新率都是 60 次/秒。因此,如果在页面中有一个动画或渐变效果,或者用户正在滑动页面,那么浏览器渲染动画或页面的每一帧的速率,也需要和设备屏幕的刷新率保持一致。
也就是说,浏览器对每一帧画面的渲染工作需要在 16 毫秒内完成。但实际上,在渲染某一帧画面的同事,浏览器还有一些额外的工作要做(比如渲染队列的管理,渲染线程与其他线程之间的切换等等)。因此单纯的渲染工作一般需要控制在 10 毫秒之内完成,才能达到流畅的视觉效果。如果超过了这个时间限度,页面的渲染就会出现卡顿,也就是常说的 jank。
webp 与 base64
图片大小对内存的影响
Script - Style - Layout - Paint - Composite
几种情况
JavaScript -- Style -- Layout -- Paint -- Composite
JavaScript -- Style -- (Layout) -- Paint -- Composite
如果修改了一个 DOM 元素的 paint only 属性,比如背景图片、文字颜色或阴影等,这些属性不会影响页面的布局,因此浏览器会在完成样式计算之后,跳过布局过程,只做绘制渲染层合并过程。
JavaScript -- Style -- (Layout -- Paint) -- Composite
如果修改一个非样式且非绘制的 css 属性,那么浏览器会在完成样式计算后,跳过布局和绘制的过程,直接做渲染层合并,
这种方式在性能上是最理想的,对于动画和滚动这种负荷很重的渲染,我们要争取使用这种渲染流程。
优化
Script 优化
错误的执行时机和太长的时间消耗,是常见的导致 js 性能低下的原因。尽量从这两方面对 js 代码带来的执行性能的影响入手。
减少错误的执行时机
setTimeout
或setInterval
,请使用requestAnimationFrame
减少时间消耗
Style 优化
将 CSS 置于文档 head 标签内
尽早在 HTML 文档内指定所有 CSS 资源,以便浏览器尽早发现 标记并尽早发出 CSS 请求。
避免使用 CSS import
一个样式表可以使用 CSS import (@import) 指令从另一样式表文件导入规则。不过,应避免使用这些指令,因为它们会在关键路径中增加往返次数:只有在收到并解析完带有 @import 规则的 CSS 样式表之后,才会发现导入的 CSS 资源。
内联阻塞渲染的 CSS
为获得最佳性能,您可能会考虑将关键 CSS 直接内联到 HTML 文档内。这样做不会增加关键路径中的往返次数,并且如果实现得当,在只有 HTML 是阻塞渲染的资源时,可实现“一次往返”关键路径长度。
降低样式选择器的复杂度
使用基于 class 的方式,比如 BEM
减少需要执行样式计算的元素个数
通过 class 或 cssText 的方式批量修改样式
Layout 优化
触发条件
DOM 元素的“几何属性”的修改
比如 width/height/left/top 等,都需要重新计算布局
读取样式属性,强制同步布局
如果在读取样式属性之前,修改了容器的样式,那么会强制触发重绘。
优化方案
DOM
visibility: hidden
布局
使用 flexbox 替代老的布局模型
flexbox 的性能更高
图片在渲染前指定大小:因为 img 元素是内联元素,所以在加载图片后会改变宽高,严重的情况会导致整个页面重排,所以最好在渲染前就指定其大小,或者让其脱离文档流
避免强制同步布局事件的发生
我们可以强制浏览器在执行 js 脚本之前先执行布局过程,这就是所谓的强制同步布局。
在 js 运行的时候,它能获取到的元素样式属性值都是上一帧画面的,都是旧值。
如果在读取 height 属性之前,修改了容器的样式,那么会强制触发重绘。
为了避免触发不必要的布局过程,应该首先批量读取元素样式属性(浏览器将直接返回上一帧的样式属性值),然后再对样式属性进行写操作。
避免快速连续的布局
这种情况更糟糕,看代码:
在下一次循环中读取 box 的属性值时,浏览器必须先使上一次循环中的样式更新操作生效,也就是执行布局过程,然后才能响应本次循环中的样式读取操作,也就意味着,布局过程将在每次循环中发生。
优化:
Paint
Paint(绘制)其实是生成元素呈现的像素的过程。例如,一个有着灰色背景、有文字的元素,当浏览器 Paint 时,是决定哪些像素填充背景、哪些像素填充文字,然后浏览器将这些像素存入位图(Bitmap)中。
Paint 是代价最高的一步,因此尽量减少 Paint 的时间,甚至避免 Paint 的发生,对页面性能的提升有很重要的作用。
如何触发
如何优化
提升元素渲染层为合成层。
需要注意的是,用
transform
和opacity
时,如果元素被提升到了独立的合成层,不会触发 Paint;但如果没有提升,还是会触发。Composite
由于重叠的原因,可能随便就能产生大量的合成层,而每个合成层要消耗 CPU 和内存。浏览器考虑到了,于是有了层压缩。但不是万能的,有很多情况下,浏览器是无法进行层压缩的。这些也是我们应该避免的。
提升为合成层有以下好处:
合成层的位图,会交由 GPU 合成,比 CPU 处理要快。
当需要 repaint 时,只需要 repaint 本身,不会影响到其他层。
对于 transform 和 opacity 效果,不会触发 layout 和 paint。
对于 fixed 的合成层,移动时不会触发 repaint。
对页面中可能发生大量重排重绘的元素单独触发渲染层,使用 GPU 分担 CPU 压力。但也是对 GPU 有压力的,通常情况下,我们会对动画元素采取硬件加速。
用合成层做动画
优先:will-change
其次:transform:translateZ(0);
用 transform 或 opacity 实现动画
他们仅触发 composite。前提是元素提升为 合成层。
减少绘制区域
防止层爆炸
解决方案是打破 overlap 的条件,也就是让其他元素不要和合成层元素层叠。
z-index
感知性能
使用骨架屏
白屏感知
页面可见时间
重要内容可见时间
白屏时间和补救方法
尽量同步输出,mock,placeholder
内存
chrash
其他
关于页面加载过程中的各个事件
参考资料:https://developers.google.com/web/fundamentals/performance/critical-rendering-path/measure-crp?hl=zh-cn
domLoading
: 这是整个过程的起始时间戳,浏览器即将开始解析第一批收到的 HTML 文档字节domInteractive
: 表示浏览器完成对所有 HTML 的解析并且 DOM 构建完成的时间点。domContentLoaded
: 一般表示 DOM 和 CSSOM 均准备就绪,且没有 阻塞解析的 js 的时间点domComplete
: 顾名思义,所有处理完成,并且网页上的所有资源(图像等)都已下载完毕,也就是说,加载转环已停止旋转loadEvent
: 作为每个网页加载的最后一步,浏览器会触发 onload 事件,以便触发额外的应用逻辑httpDNS
httpDNS 是什么
httpDNS 是面向无线端的域名解析服务,与传统的走 UDP 协议的 DNS 不同,httpDNS 基于 HTTP 协议。
PC 端的服务一般通过浏览器访问,对于域名解析,浏览器只会走标准 DNS,我们没法干预。无线端的可操作空间比较大,APP 可以自己构造并发送 HTTP 请求,所以能够做到精细化的控制,其中就包括域名解析。
使用 httpDNS 解析域名的基本流程如下:
1、客户端向 httpDNS 服务端发送普通的 HTTP 请求,请求中携带需要解析的域名。
2、httpDNS 服务端解析出域名对应的 ip 地址,将解析结果封装为 JSON 格式,返回给客户端。
和传统 DNS 一样,httpDNS 的解析结果中包含 TTL,客户端可以根据 TTL 将结果缓存一段时间。
httpDNS 的优势
防止域名劫持
传统 DNS 由 Local DNS 解析域名,不同运营商的 Local DNS 由不同的策略,某些 Local DNS 可能会劫持特定的域名,
采用 httpDNS 可以绕过 Local DNS,避免被劫持;
另外,httpDNS 的解析结果包含 HMAC 校验,也能够防止解析结果被中间网络设备篡改。
更精准的调度
对域名解析而言,尤其是 CDN 域名,解析得到的 IP 应该更靠近客户端的地区和运营商,这样才能有更快的网络访问速度。然而,由于运营商策略的多样性,其推送的 Local DNS 可能和客户端不在同一个地区,这时得到的解析结果可能不是最优的。httpDNS 能够得到客户端的出口网关 IP,从而能够更精确地判断客户端的地区和运营商,得到更精准的解析结果。
更小的解析延迟和波动
在 2G/3G 这种移动网络下,DNS 解析的延迟和波动都比较大。就单次解析请求而言,httpDNS 不会比传统的 DNS 更快,但通过 httpDNS 客户端 SDK 的配合,总体而言,能够显著降低解析延迟和波动。httpDNS 客户端 SDK 有几个特性:预解析、多域名解析、TTL 缓存和异步请求。
额外的域名相关信息
传统 DNS 的解析结果只有 ip,httpDNS 的解析结果采用 JSON 格式,除了 ip 外,还支持其他域名相关的信息,比如端口、spdy 协议等。利用这些额外的信息,APP 可以启用或停止某个功能,甚至利用 httpDNS 来做灰度发布,通过 httpDNS 控制灰度的比例。
httpDNS 如何解析一个域名
httpDNS 服务端并没有“域名和 ip 对应关系”的数据库,它做了一次协议转换,从 HTTP 转换为 DNS,将域名解析请求转发给后端 DNS。
请求过程是这样的:客户端--(HTTP)-- httpDNS 服务器 --(DNS)-- DNS服务器
后端 DNS 服务器需要拿到客户端的来源 ip,如何透传 httpDNS 服务器呢?httpDNS 使用了 Google 提出的 DNS 扩展选项 edns-client-subnet,通过该选项,可以将客户端 ip 放在 DNS 报文中传给后端 DNS,只要后端 DNS 支持该选项,它就会从 DNS 报文中提取客户端 ip。 httpDNS 对接的后端 DNS 都支持 edns-client-sub 选项。
具体而言,httpDNS 服务端的处理过程如下:
1、接收客户端发来的 HTTP 请求,请求中携带域名列表。
2、解析请求的内容,检查安全校验值,执行白名单过滤,得到待解析的域名列表。
3、针对域名列表中的每一个域名,构造 DNS 请求报文,发往对应的后端 DNS。
4、等待并收集 DNS 响应报文,解析报文得到 IP 地址,最后,每个域名都有了解析结果或已经超时。
5、对每个域名,将解析得到的 IP,以及在 httpDNS 上配置的其他信息,添加到 JSON 报文中。
6、根据 JSON 报文计算 HMAC 校验值,将校验值和 JSON 报文发给客户端。
参考资料
The text was updated successfully, but these errors were encountered: