从 URL 输入,到页面完全展示,整个过程性能消耗
HTTP 层面优化
- DNS 解析 DNS 实现域名到 IP 的映射,通过域名访问站点,每次请求都要做 DNS 解析。一般采用 DNS Prefetch,当你浏览网页时,浏览器会在加载网页时对网页中的域名进行解析缓存,这样当用户点击网页中的链接时就无需进行 DNS 解析,减少用户等待时间
<!-- 只有部分浏览器支持 --> <link rel="dns-prefetch" href="..." />
TCP 链接 采用 HTTP2.0,可以复用 TCP 通道,采用二进制格式而非文本格式,使用报头压缩,HTTP2降低了开销
减少 HTTP 请求次数,将多个请求合并成一个,减少 HTTP 的开销
- Gzip 压缩
图片优化
图片格式
jpeg | jpg 优点:很高的压缩比,而且画质和色彩还能较好的保存 缺点:压缩比较高,如果想要展示清晰纹理和边缘的图片,会有锯齿感,比如logo 使用场景:需要展示比较大的图片,还想保存画质效果的时候,比如轮播图
png 优点:可以做透明背景的图片,还可以强调纹理和边缘,弥补了 jpg 的缺点 缺点:细节保存好所以体积较大,色彩上和 jpg 不相上下 使用场景:贴图,小图片等
svg 优点:文本文件,体积更新,压缩性更强,图片可以无限放大且不失真,兼容性好 缺点:渲染成本高,有一定的学习成本 使用场景:icon,font
base64 优点:依赖编码,将编码结果写入 HTML 或者 CSS,减少请求次数 缺点:base64 编码后,图片大小会膨胀为原来的 4/3 使用场景:小图标解决方案,非常小的 logo
WebP (google 提出的一种新的图片格式) 优点:WebP 像 jpeg 同样对细节丰富的图片信手拈来,像 png 同样支持透明,像 gif 同样能够显示动态图片。它集多种图片文件格式的优势于一身。 缺点:兼容性
图片加载优化
- 懒加载
- 渐进式图片
- 响应式图片
渲染优化
复合线程与图层
复合:把页面拆解成不同的图层,当某一图层变化时,不影响其他图层的渲染,这样绘制的过程更高效
- 复合线程在做什么:将页面拆分为图层进行绘制再进行复合
- 默认情况下,浏览器根据自己的规则进行拆分,主要分析元素和元素之间是否会互相影响,如果某一个元素会对其他元素造成过多的影响,则最好将其提取
- 也可以自己指定图层(z-index)
- 利用 DevTools 了解网页的图层拆分情况
- 哪些样式仅影响复合,不触发重绘和回流:
- Position: transform: translate(npx, npx)
- Scale: transform: scale(n)
- Rotation: transform: rotate(ndeg)
- Opacity: opacity: 0…1
尽量使用 transform 利用 GPU 加速: GPU 加速通常包括:Canvas2D,布局合成,css3转换(transition,transform),WebGL 和 video
代码优化
JavaScript 代码优化
- 引用 JavaScript 库的优化
- Code Splitting(代码拆分,按需加载)
- Tree Shaking 代码减重
- 减少主线程的工作量
- 避免长任务
- 避免超过 1kb 的行间脚本,因为行间脚本浏览器引擎无法进行优化
- 使用 rAF 和 rLC进行时间调度(requestAnimationFrame、requestIdleCallback)
对象优化
- 对象属性初始化顺序尽量保持一致。JS 是若类型的语言,引擎在解析的时候,会根据自己的推断进行解析,JS 的隐藏类型有 21 中,声明对象、初始化值的时候会创建隐藏类型,隐藏类型会根据顺序进行缓存,如果下次赋值有新的属性或属性的赋值顺序发送变化,则缓存失效
- 对象实例化后尽量避免对其添加新的属性。实例化时已经存在的属性被称为
In-Object
属性,后添加的属性称为Normal/Fast
属性,存储在property store
里,需要通过描述数组间接查找,这样效率低 - 尽量使用
Array
来代替Array-like
对象。类数组对象虽然有索引、length 属性,但它不是数组,没有forEach
等方法,JS 引擎对数组有性能优化而对类数组没有,所以大量操作的时候可以将类数组转为数组对象再操作 - 避免越界访问数组。越界的那个位置的值是 undefined,数组也是对象,如果在当前位置找不到,则会沿着原型链向上找,这会造成额外的开销(差距为6倍)
- 避免元素类型转换。当数组中元素的类型相同的时候,JS 引擎会给这个数组标记一个元素类型,并且有相应的优化
HTML 优化
- 减少 iframe 的使用。额外添加的页面加载会阻塞原文档的加载,也就是说 onload 必然发生在 iframe 加载完成之后,而且在一个页面中使用 iframe 加载另一个页面比直接渲染另一个页面的开销要大。如果一定要使用 iframe,则考虑在 onload 事件之后为其添加 src 属性
- 压缩空白符。空白符虽然方便开发人员阅读,但是占用文档空间,所以在打包的时候最好去掉这些多余的空白符
- 避免节点的深层嵌套。节点层级越深,生成的 DOM 树就越深,遍历起来就越复杂
- 避免使用 table 布局。使用起来不灵活,开销也大
- CSS/JavaScript 尽量使用外链。写在行间会造成 HTML 文档过大,而且引擎不好做优化
CSS 优化
- 降低 CSS 对渲染的阻塞。尽量早的下载和解析 CSS
- 减少 CSS 文件的大小。按需加载 CSS,一开始只加载用到的CSS文件,而没有用到的文件则延迟下载解析
- 利用 GPU 完成动画。
- 使用CSS的contain属性。contain 属性的主要目的是隔离指定内容的样式、布局和渲染。开发人员可以使用这个 contain 属性来限制指定的DOM元素和它的子元素同页面上其它内容的联系
- 使用font-display属性。让文字尽早展示,且减轻文字闪动的问题