项目性能优化

1. 编码优化

编码优化,指的就是 在代码编写时的,通过一些 最佳实践,提升代码的执行性能。通常这并不会带来非常大的收益,但这属于 程序猿的自我修养,而且这也是面试中经常被问到的一个方面,考察自我管理与细节的处理。

数据读取:
通过作用域链 / 原型链 读取变量或方法时,需要更多的耗时,且越长越慢;
对象嵌套越深,读取值也越慢;
最佳实践:

  • 尽量在局部作用域中进行 变量缓存;
  • 避免嵌套过深的数据结构,数据扁平化 有利于数据的读取和维护;

循环: 循环通常是编码性能的关键点;
代码的性能问题会再循环中被指数倍放大;
最佳实践:

  • 尽可能 减少循环次数;
  • 减少遍历的数据量;
  • 完成目的后马上结束循环;
  • 避免在循环中执行大量的运算,避免重复计算,相同的执行结果应该使用缓存;
  • js 中使用 倒序循环 会略微提升性能;
  • 尽量避免使用 for-in 循环,因为它会枚举原型对象,耗时大于普通循环;

条件流程性能: Map / Object > switch > if-else

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 使用 if-else
if(type === 1) {

} else if (type === 2) {

} else if (type === 3) {

}

// 使用 switch
switch (type) {
case 1:
break;4
case 2:
break;
case 3:
break;
default:
break;
}

// 使用 Map
const map = new Map([
[1, () => {}],
[2, () => {}],
[3, () => {}],
])
map.get(type)()

// 使用 Objext
const obj = {
1: () => {},
2: () => {},
3: () => {},
}
obj[type]()

dom 优化:
减少访问 dom 的次数,如需多次,将 dom 缓存于变量中;
减少重绘与回流:

  • 多次操作合并为一次;
  • 减少对计算属性的访问;
  • 例如 offsetTop, getComputedStyle 等
  • 因为浏览器需要获取最新准确的值,因此必须立即进行重排,这样会破坏了浏览器的队列整合,尽量将值进行缓存使用;
  • 大量操作时,可将 dom 脱离文档流或者隐藏,待操作完成后再重新恢复;
  • 使用DocumentFragment / cloneNode / replaceChild进行操作;

使用事件委托,避免大量的事件绑定;

css 优化:
层级扁平,避免过于多层级的选择器嵌套;
特定的选择器 好过一层一层查找: .xxx-child-text{} 优于 .xxx .child .text{}
减少使用通配符与属性选择器;
减少不必要的多余属性;
使用动画属性 实现动画,动画时脱离文档流,开启硬件加速,优先使用 css 动画;
使用link替代原生@import;

html 优化:
减少 dom 数量,避免不必要的节点或嵌套;
避免空标签,能减少服务器压力,因为 src 为空时,浏览器仍然会发起请求

  • IE 向页面所在的目录发送请求;
  • Safari、Chrome、Firefox 向页面本身发送请求;
  • Opera 不执行任何操作。

图片提前 指定宽高 或者 脱离文档流,能有效减少因图片加载导致的页面回流;
语义化标签 有利于 SEO 与浏览器的解析时间;
减少使用 table 进行布局,避免使用br标签与hr标签;

2. 页面基础优化

引入位置: css 文件head标签中引入, js文件body标签底部引入;

  • 影响首屏的,优先级很高的 js 也可以头部引入,甚至内联;

减少请求 (http 1.0 - 1.1),合并请求,正确设置 http 缓存;
减少文件体积:

  • 删除多余代码:
  • tree-shaking
  • UglifyJs
  • code-spliting
  • 混淆 / 压缩代码,开启 gzip 压缩;
  • 多份编译文件按条件引入:
  • 针对现代浏览器直接给 ES6 文件,只针对低端浏览器引用编译后的 ES5 文件;
  • 可以利用script type=”module” / script type=”module”进行条件引入用
  • 动态 polyfill,只针对不支持的浏览器引入 polyfill;

图片优化:

  • 根据业务场景,与UI探讨选择 合适质量,合适尺寸;
  • 根据需求和平台,选择 合适格式,例如非透明时可用 jpg;非苹果端,使用 webp;
  • 小图片合成 雪碧图,低于 5K 的图片可以转换成 base64 内嵌;
  • 合适场景下,使用 iconfont 或者 svg;

使用缓存:
浏览器缓存: 通过设置请求的过期时间,合理运用浏览器缓存;
CDN缓存: 静态文件合理使用 CDN 缓存技术;

  • HTML 放于自己的服务器上;
  • 打包后的图片 / js / css 等资源上传到 CDN 上,文件带上 hash 值;
  • 由于浏览器对单个域名请求的限制,可以将资源放在多个不同域的 CDN 上,可以绕开该限制;

服务器缓存: 将不变的数据、页面缓存到 内存 或 远程存储(redis等) 上;
数据缓存: 通过各种存储将不常变的数据进行缓存,缩短数据的获取时间;

3. 首屏渲染优化

css / js 分割,使首屏依赖的文件体积最小,内联首屏关键 css / js;
非关键性的文件尽可能的 异步加载和懒加载,避免阻塞首页渲染;
使用dns-prefetch / preconnect / prefetch / preload等浏览器提供的资源提示,加快文件传输;
谨慎控制好 Web字体,一个大字体包足够让你功亏一篑;

  • 控制字体包的加载时机;
  • 如果使用的字体有限,那尽可能只将使用的文字单独打包,能有效减少体积;

合理利用 Localstorage / server-worker 等存储方式进行 数据与资源缓存;
分清轻重缓急:

  • 重要的元素优先渲染;
  • 视窗内的元素优先渲染;

服务端渲染(SSR):

  • 减少首屏需要的数据量,剔除冗余数据和请求;
  • 控制好缓存,对数据/页面进行合理的缓存;
  • 页面的请求使用流的形式进行传递;

优化用户感知:

  • 利用一些动画 过渡效果,能有效减少用户对卡顿的感知;
  • 尽可能利用 骨架屏(Placeholder) / Loading 等减少用户对白屏的感知;
  • 动画帧数尽量保证在 30帧 以上,低帧数、卡顿的动画宁愿不要;

js 执行时间避免超过 100ms,超过的话就需要做:

  • 寻找可 缓存 的点;
  • 任务的 分割异步 或 web worker 执行;