前言#
在前端,性能优化是离不开的话题。性能优化可以从两个大的方面去着手,分别是网络层面和渲染层面。网络层面主要是 HTTP 请求的优化,而渲染层面则是浏览器渲染过程中的优化。
网络层面#
网络层面优化即 HTTP 优化。我们知道 HTTP 访问的开销是非常大的,非常消耗资源。如何有效的优化 HTTP 访问,对于性能有非常大的作用。
主要分为两个方面:资源的压缩和合并,即减少 HTTP 请求次数和减少 HTTP 单次请求所花费的时间。
Webpack#
Webpack 是前端最流行的打包工具之一。在打包过程中,可以进行一下优化
- DllPlugin:使用 DllPlugin 插件,把第三方库单独打包到一个文件中,减少打包体积。
- Happypack:将 Loader 由单进程转为多进程,提高打包的效率。
- Shaking:自动删除不需要的代码,减少代码体积。
- Gzip: 压缩代码。
图片优化#
图片在静态资源中,使用非常广泛,而且体积是非常大的。优化图片资源,可以有效得减少代码的体积。
-
适当的格式
图片格式可以分为无压缩、无损压缩和有损压缩集中。根据不同图片格式的特点,选择正确的图片格式,可以有效的减少图片大小。
-
gif:无损压缩格式。体积较小,而且支持透明和动画,但是只存储 8 位索引,不适合色彩复杂、细节丰富的图片,对于色彩简单的 logo 和 icon 比较合适。
-
jpg:有损压缩格式。体积小,不支持透明度处理,但是人类肉眼不容易发现损失的色彩,适合色彩丰富的图片。使用场景比如大的图片,轮播图等。
-
png:无损压缩格式。体积大,png-8 最多支持 256 种颜色,png-24 最多支持 1600 万种颜色,支持透明度处理,高品质,非常适合用于源文件或需要二次编辑的图片格式的保存,比如网站大的主 logo 等。
-
webp:新的图像格式,由 Google 开发。与 png、jpg 相比,相同的视觉体验下,WebP 图像的尺寸缩小了大约 30%。另外,WebP 图像格式还支持有损压缩、无损压缩、透明和动画。理论上完全可以替代 png、jpg、gif 等图片格式,当然目前 webp 的还没有得到全面的支持。
-
svg:矢量图,文件体积更小,可压缩性更强。图片可无限放大而不失真。是文本文件,可以像写代码一样编辑,但是渲染成本比较高,还有需要学习成本。
-
-
图片压缩和优化
- 使用 base64 压缩体积小的图片,减少 HTTP 请求次数。
- 使用适当宽度的图片,减少不必要的体积。
- 使用 css 代替一些简单的图片效果。
- 使用雪碧图,把多张图片合并在一起,减少图片体积和 HTTP 请求次数。
HTTP 缓存#
主要分为强缓存和协商缓存。请查看浏览器缓存浅析文章。
浏览器存储#
浏览器存储把一些不常更新的资源存储在浏览器,需要时候直接从本地获取,减少不必要的 HTTP 访问。
主要分为 LocalStorage ,SessionStorage 和 IndexDB。下面表格中展示本地存储的一些区别和特性。
| 特性 | cookie | LocalStorage | SessionStorage | IndexDB |
|---|---|---|---|---|
| 由谁初始化 | 客户端或服务器,服务器可以使用Set-Cookie请求头。 | 客户端 | 客户端 | 客户端 |
| 数据生命周期 | 一般由服务器生成,可以设置过期时间 | 除非被清理,否则一直存在 页面关闭就清理 | 当前页面关闭时清理掉 | 除非被清理,否则一直存在 页面关闭就清理 |
| 数据存储大小 | 4K | 5M | 5M | 无限 |
| 与服务端通信 | 每次都会携带在 header 中,对于请求性能影响 | 不参与 | 不参与 | 不参与 |
| 访问权限 | 任意窗口 | 任意窗口 | 当前页面窗口 | 任意窗口 |
CDN#
CDN 的全称是 Content Delivery Network,即内容分发网络。CDN 是构建在网络之上的内容分发网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN 的关键技术主要有内容存储和分发技术。
CDN 主要存储的是静态资源。比如静态的 JS、CSS 和图片等资源。还可以使用多个不同的 CDN 域名来存储,突破浏览器并发限制,节约 Cookie 带宽,节约主域名的连接数, 优化页面响应速度,防止不必要的安全问题等。
HTTP2#
HTTP2 比 HTTP1 的性能更好,但是现在还没有完全普及。
渲染层面#
渲染层面的渲染指的是浏览器渲染,这里主要指的是浏览器的渲染过程的优化。
理解浏览器渲染机制#
需要理解浏览器的渲染机制原理后,才能更好的优化渲染过程。
CSS 优化
-
CSS 选择器的读取是从右到左的,一般的话,如果使用 class 或者 id 选择器后,前面就不用画蛇添足的使用标签选择器了。
-
CSS 的放置位置。
CSS 的样式放在 head 标签中。把
<link>标签放在<head>里面是规范要求的内容。此外,这种做法可以让页面逐步呈现,提高了用户体验。将样式表放在文档底部附近,会使许多浏览器(包括 Internet Explorer)不能逐步呈现页面。一些浏览器会阻止渲染,以避免在页面样式发生变化时,重新绘制页面中的元素。这种做法可以防止呈现给用户空白的页面或没有样式的内容。 -
使用 CDN 加速 CSS 静态文件。
JavaScript 优化
-
JavaScript 代码的放置位置。
把
<script>标签恰好放在</body>之前,脚本在下载和执行期间会阻止 HTML 解析。把<script>标签放在底部,保证 HTML 首先完成解析,将页面尽早呈现给用户。例外情况是当你的脚本里包含document.write()时。但是现在,document.write()不推荐使用。同时,将<script>标签放在底部,意味着浏览器不能开始下载脚本,直到整个文档(document)被解析。也许,对此比较好的做法是,<script>使用defer属性,放在<head>中。 -
适当使用
async/defer异步加载。-
<script>- HTML 解析中断,脚本被提取并立即执行。执行结束后,HTML 解析继续。 -
<script async>- 脚本的提取、执行的过程与 HTML 解析过程并行,脚本执行完毕可能在 HTML 解析完毕之前。当脚本与页面上其他脚本独立时,可以使用async,比如用作页面统计分析。 -
<script defer>- 脚本仅提取过程与 HTML 解析过程并行,脚本的执行将在 HTML 解析完毕后进行。如果有多个含defer的脚本,脚本的执行顺序将按照在 document 中出现的位置,从上到下顺序执行。
注意:没有
src属性的脚本,async和defer属性会被忽略。从应用的角度来说,一般当我们的脚本与 DOM 元素和其它脚本之间的依赖关系不强时,我们会选用 async。当脚本依赖于 DOM 元素和其它脚本的执行结果时,我们会选用 defer。
-
-
优化 JavaScript 代码。尽量使用时间复杂度低的算法代码,提高代码性能。
-
使用 CDN 加速 JavaScript 静态文件。
DOM 优化#
原则:减少 DOM 的操作。
减少 DOM 的操作
优先使用 JavaScript 处理,浏览器的 JavaScript 引擎和渲染引擎时独立实现的,通过桥接接口来连接。
通过 JavaScript 操作 DOM 需要昂贵的“过桥费”,尽量减少 DOM 的操作。
优化 DOM 的操作
- 缓存 DOM 对象。
- 在内存中操作元素。
- 一次性 DOM 节点生成。
- 通过类修改样式,而不是直接修改样式(多次赋值影响性能)。
- 通过事件代理批量操作事件。
规避重绘和回流#
重绘和回流是非常消耗性能的,需要尽量减少重绘和回流的操作。以下几个方面可以注意下
CSS 方面
-
使用 transform 替代 top
-
使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局
-
避免使用 table 布局,可能很小的一个小改动会造成整个
table的重新布局。 -
尽可能在 DOM 树的最末端改变 class,回流是不可避免的,但可以减少其影响。尽可能在 DOM 树的最末端改变 class,可以限制了回流的范围,使其影响尽可能少的节点。
-
避免设置多层内联样式,CSS 选择符从右往左匹配查找,避免节点层级过多。
<div>
<a> <span></span> </a>
</div>
<style>
span {
color: red;
}
div > a > span {
color: red;
}
</style>对于第一种设置样式的方式来说,浏览器只需要找到页面中所有的span标签然后设置颜色,但是对于第二种设置样式的方式来说,浏览器首先需要找到所有的span标签,然后找到span标签上的a标签,最后再去找到div标签,然后给符合这种条件的span标签设置颜色,这样的递归过程就很复杂。所以我们应该尽可能的避免写过于具体的 CSS 选择器,然后对于 HTML 来说也尽量少的添加无意义标签,保证层级扁平。
-
将动画效果应用到 position 属性为 absolute 或 fixed 的元素上,避免影响其他元素的布局,这样只是一个重绘,而不是回流,同时,控制动画速度可以选择
requestAnimationFrame,详见探讨 requestAnimationFrame。 -
避免使用 CSS 表达式,可能会引发回流。
-
将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点,例如
will-change、video、iframe等标签,浏览器会自动将该节点变为图层。 -
CSS3 硬件加速(GPU 加速),使用 css3 硬件加速,可以让
transform、opacity、filters这些动画不会引起回流重绘 。但是对于动画的其它属性,比如background-color这些,还是会引起回流重绘的,不过它还是可以提升这些动画的性能。 -
对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。
JavaScript 方面
-
避免频繁操作样式,最好一次性重写
style属性,或者将样式列表定义为class并一次性更改class属性。 -
避免频繁操作 DOM,创建一个
documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。 -
避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。主要包括以下属性或方法:
offsetTop、offsetLeft、offsetWidth、offsetHeightscrollTop、scrollLeft、scrollWidth、scrollHeightclientTop、clientLeft、clientWidth、clientHeightwidth、heightgetComputedStyle()getBoundingClientRect()
异步更新策略#
在我们使用前端框架(Vue, React)时,修改数据(状态)时,并不是单次修改完后,立即同步改变修改的数据(状态)。而是一个异步的过程,框架会把多此修改后合并成一次统一修改,这样就可以减少数据的频繁更新操作,提高性能。
服务端渲染#
服务端渲染是服务器直接返回 HTML 字符串给浏览器,直接渲染到页面上。服务端渲染可以有效改善 SEO 问题和提高首屏加载速度。但是,服务端渲染虽然可以提高浏览器的渲染效率,但会加重服务器负担,应该谨慎使用。
一些应用#
懒加载#
一次性把所有的图片资源加载完毕,那么很可能会造成白屏、卡顿等现象。使用懒加载一步一步的展示出来,即可以减少性能压力,也不影响用户体验。
防抖和节流#
频繁触发回调导致的大量计算会引发页面的抖动甚至卡顿,使用防抖和节流来控制事件被触发的频率,提高性能。
可视化性能监测#
- Performance 面板工具。Performance 是 Chrome 提供给我们的开发者工具,用于记录和分析我们的应用在运行时的所有活动。它呈现的数据具有实时性、多维度的特点,可以帮助我们很好地定位性能问题。
- LightHouse 插件。生成测试报告,内容非常丰富,还可以给出了可行的建议。
- performance API。浏览器自带的
window.performanceAPI,可以获取一些较高准确度的数据,比如timing等。