skip to content
usubeni fantasy logo Usubeni Fantasy

前端项目优化自查

/ 8 min read

  • 性能问题的三大台柱:网络、渲染、运行
  • 高速度的加载是抓住用户的利器,无论内容做得多好,用户因为等待时间太长而离开就没有意义了
  • 程度需要把握,把阴阳平衡铭刻于心
  • 优化时应使用浏览器的匿名模式防止插件干扰
  • 虽然单个优化的空间真的很小,但是积小成多,编写高效程序基本不会错

工具

Core Web Vitals

指标

TTI FID LCP TBT CLS FMP

加载优化

连接

  • 域名分片,增加可同时进行的请求
  • 使用 HTTP 协议的缓存功能(服务器负责),相关传送门 HTTP 缓存简析
  • 使用两个 storage 缓存 ajax 数据,需要小心数据过时
  • 使用 service worker 缓存文件(PWA 解决方案)
  • 使用 CDN
  • 减少 cookie 体积(JWT 尤其注意)
  • 负载均衡等手段(正常来说,不归前端管)
  • 启用 keep-alive(服务器负责)
  • 开启 gzip 甚至 Brotli(服务器负责)

减少链接数

  • base64 内嵌图片,可以减少请求,但是 base64 比原数据大
  • iconfont 代替单个图标文件图标,大幅减少请求
  • 代码合并,减少代码文件数量

大杀器

  • HTTP2,需要注意的是使用 HTTP2 之后,因为并行下载资源,域名分片,雪碧图,base64 内嵌,代码合包统统没有必要,分开小文件下载反而有早下完早使用的好处

HTML

  • 明白 critical rendering path 的概念
  • 默认情况 HTML 文件 parse 到 JavaScript 会停下来下载、然后运行,然后继续 parse
  • 使用 defer 属性:下载时 parse 不停止,下载完等待 HTML parse 完成再运行
  • 使用 async 属性:下载时 parse 不停止,下载完立即运行
  • 善用 <link> 标签的 preload 等属性

defer 和 async:可以理解为 defer 是 defer(延迟)到文档加载完成,用于强调运行顺序或需要整个 DOM 的脚本;async 就是在 parse html 的同时异步加载,下载完立即运行,适用于广告和统计之类的功能。

CSS

  • 不要使用 @import,因为从 css 里下载会阻塞当前 css 解释,从而会阻塞渲染进程
  • 使用 font-display 优化大型字体文件加载前的显示
  • 借助 devtools 的 Coverage 面板鉴别关键 CSS
  • media 属性条件加载 CSS
  • 避免 synchronous layout
  • 适当拆分 layer,但是 layer 也是耗内存的,不要过分依赖
.moving-element {
will-change: transform;
}

打包优化

  • 代码拆分,这似乎与减少连接数的合并互斥,其实也不是,这个拆分是把现在不需要用的代码延后加载,在 SPA 中十分有效
  • 调整 webpack 配置,根据现实需求压缩代码、实现代码合并
  • 打包时可以考虑 tree-shaking 与 scope hoisting
  • 抽离关键代码用于首屏渲染

静态数据体积

运行优化

https://medium.com/jspoint/how-the-browser-renders-a-web-page-dom-cssom-and-rendering-df10531c9969

HTML

  • 减少 DOM 复杂度(如高频使用开源重型组件,考虑自己实现最简化的替代品)
  • 无障碍优化(Accessibility)

JavaScript

  • 老掉牙的事件委托问题
  • 防抖节流
  • 内存泄漏问题,参考文章:Beyond Memory Leaks in JavaScript
  • 减少 DOM 操作可能引起的重排与重绘
  • SSR,以新的技术栈回到服务器渲染的初心
  • 因为渲染和脚本运行是互斥的,如果脚本运行时间太长,用户滚动页面时就会因为没有足够的渲染时间而使页面卡顿,所以运行大型任务时如果需要保持页面流畅,可以把任务拆分成 16.7 ms 内完成的多个任务,然后使用 requestAnimationFrame 运行,保证页面不卡顿
  • 上一个问题也可以用 web worker 解决,但是数据交流不一定方便
  • lazy load 重型组件(IntersectionObserver)
帧的组成

图片来自 aerotwist

Minimize main thread work

CSS

  • 少用通配符
  • CSS 渲染原理以及优化策略
  • 编写高效 CSS
  • 理解 Recalculate Styles、回流(reflow)和重绘(repaint)
  • CSS 硬件加速(增加 composite 的层减少回流重绘)
  • 在未来,你可以用 Worklets 对浏览器渲染部分进行编程操作

可以在 csstriggers 查看 CSS 属性是否触发回流或重绘

代码可读性

  • 使用固定 ESlint 为团队统一代码风格(自带不少最佳实践)
  • 保持变量名准确描述变量信息
  • 把小黄鸭调试法用在写代码中,思考别人在看代码时会有什么疑惑,并记下
  • 在 workaround 写下注释,避免之后不解,甚至是自己都不解
  • 在可以优化的地方打上标记,如 // TODO:
  • 减少嵌套,拆分大型表达式
  • 把变量控制在最小范围的作用域里

性能检测方式

另一个思路

制作极速版

如果发现极速版用户明显比原版高,那么原版也可以考虑取消一些不常用功能。

不一定要写两个版本,你完全可以新建一个环境,然后使用 if(process.env.NODE_ENV !== 'blitz') 的方式筛选部分大型逻辑

更进一步,可以通过后台开关配置前端功能是否启用,这是一个使产品更稳定的方法:

  • 对于新功能,在遇到致命问题时可以迅速关闭
  • 对于性能要求高的功能可以根据环境与时机判断是否关闭

添加埋点检测功能使用量,逐步淘汰不使用或使用率低的功能

虽然不可能但不失为一个方法

  • 要求用户升级客户端

其他参考站点

评论组件加载中……