前端性能优化总结

364 阅读5分钟

前端的性能优化,说来说去也都是那些事,再发一篇也不过炒炒冷饭,但总归要自己总结一遍才能加深印象。因此这里只提供一个方向,思路的总结,没有具体的操作。

从输入url到页面加载完成发生了什么

从前端的角度,可以大致分为以下几个步骤:

  1. 用户输入网站地址,如www.baidu.com
  2. 浏览器通过DNS服务,将域名解析成IP地址
  3. 浏览器和目的IP建立TCP链接,发送HTTP请求
  4. 服务器接收并处理请求,最终返回HTTP响应报文
  5. 浏览器接收到首屏html,开始渲染
  6. 浏览器将html文档解析成DOM树
  7. 解析css,生成css规则树
  8. 将DOM树和css树合并成render树,并进行布局和绘制

从上面步骤可以看出,影响性能的主要是加载渲染。此外还有运行时的性能:脚本执行

加载优化

资源加载(网络层面)方面的性能优化主要是减小资源体积减少请求数量

减小资源体积

减小资源体积最有效的方法就是压缩。比如js混淆压缩,html压缩,图片压缩等。再加上 Gzip 神器,再压缩三分之一以上不是问题。

你会发现,实际中体积大的基本上是图片,字体这些资源,代码体积反而没那么大。有时候一不小心就把一张 1M 的图片搞到线上去了,比所有代码加起来还大。此外还要特别注意图片格式,png 格式无损压缩,有时候压缩过后还是那么大。可以换成 jpg 格式,略微的质量下降一般看不出来。我的经验是,小一点的图可以用png;背景图,banner 等可以用 jpg。

而字体的优化,在一些场景下,可以直接抽出需要的字体,文件立马小很多。

除此之外,还有按需加载,在组件库、babel-polyfill等场景应用比较多。

减少请求数量

减少请求数量,常用的方法就是缓存合并请求

效果最顶的就是浏览器缓存。通过 expiresCache-Control 进行强缓存;通过 Last-ModifiedETag 进行协商缓存,就能缓存大部分资源文件。基本上就剩文档(html)和接口的请求了。

第二种缓存是CDN。CDN 是一组分布在各个地区的服务器,它们会缓存资源副本。用户发起请求时,哪个服务器近就由其响应请求。一般存放不需要服务端进行计算,且具有访问频率高,承载流量大的资源,如图片,脚本,样式文件等静态资源。

合并请求的方案就比较多了。如针对小图标的CSS spritebase64iconfont等;通过webpack等工具将js、css打包成一个文件。但请求不是越少越好,一个大请求的速度不一定快过几个小请求,浏览器能同时发起几个请求,要充分利用这一特点。

减少请求数量还有懒加载预加载的方法。懒加载可以减少首屏的请求数量,如 import()。预加载则可以提高后续的用户体验,如预解析 dns-prefetch,预拉取 prefetch,图片预加载等。懒加载和预加载并不冲突,可以理解为不是马上需要的内容可以懒加载,即将需要的内容可以在浏览器空闲的时候预先加载。

渲染优化

提到渲染优化就不得不提回流和重绘。批量操作DOM,动画脱离文档流,requestAnimationFrame 这些方法就不多说了,容易忽略的是获取offsetLeftoffsetTop 等值的时候也可能引起回流。由于回流成本太高,浏览器会将回流操作用队列存储,当过一段时间或达到某个阈值时清空队列,执行一次回流。但当获取有些值时,浏览器会提前触发回流,以确保获取的值是正确的。

其次是非阻塞。js 文件放 body 底部;<script> 标签的 asyncdefer 属性也是大家熟悉的。

初次之外,还有针对首屏渲染优化的服务端渲染SSR,优化首屏体验的骨架屏方案,针对长列表优化的虚拟列表等。

js 代码

代码层面的优化可以说道的很多,但实际上对性能影响很小,可以说是积少成多。除非写出天怒人怨的代码。以下举几个例子:

  • 保存深层变量
  • 使用 === 而非==,避免类型转换带来的性能损耗
  • 少用 for-in
  • 使用 if-elseswitch 时,将最可能出现的情况放到前面
  • 当条件大于(等于)3个时,使用 switch。而当要判断的离散值数量很大时,使用对象或数据实现查表法
  • 尾递归
  • 避免改变词法作用域(如 evalwith
  • 减少操作 DOM
  • 事件委托
  • 删除 DOM 节点时,移除其绑定的事件
  • 用 JS 实现动画时,应使用 requestAnimationFrame
  • 防抖和节流
  • web worker

在使用框架时,还有针对框架的优化。如 Vue 中 v-for 循环 key 的绑定,Object.freeze 取消不会变更的数据的监听,切分成更小的组件以缩小更新的范围等。以及 React 中 useMemouseCallback 等缓存钩子的使用。

总的来说,学会如何排查 js 脚本中的性能问题更为重要。

总结

前端的性能优化也是针对网站加载、运行中的某一步进行优化,因此熟悉网站如何加载和运行也十分重要。只要其原理不变,那么优化的方向也不会变。雅虎军规在今天也并非一无是处。现在讨论的更多是如何在 webpack 等新工具上进行性能优化。

其次是性能优化的结果要通过指标来衡量。凭感觉去判断比原来“快了”是很不靠谱的。这种性能测量工具有很多,如鼎鼎大名的 lighthouse,这里就不多谈了。