前端优化

  • PWA:Progressive Web Apps,渐进式 Web 应用开发,旨在增强 Web 能力,缩小与原生应用的差距并创建与其类似的用户体验
  • Lighthouse:自动化测试工具,用于测试页面的性能并提出优化建议
  • SSR:Server Side Rendering,在服务端请求数据并组装 HTML 的渲染方式
  • NSR:Native Side Rendering,在客户端提前请求数据并组装 HTML 的渲染方式
  • Rehydration:俗称“注水”,复用服务端渲染生成的 DOM 结构及数据,并执行事件绑定逻辑来启动页面的过程

看起来优化手段很多,其实可以分为三个阶段 HTTP 获取资源阶段HTML 解析渲染阶段运行时优化

在结合指标进行针对性的阶段优化即可

Http 优化

HTTP 优化有两个大的方向:

  • 减少请求次数
  • 减少单次请求所花费的时间

(http 缓存) DNS tcp 连接 http 连接 解析并渲染(并逐步请求页面中的其它资源)

分析优化地方

使用谷歌 Performance 开发者工具

LightHouse

优化包的大小

webpack-bundle-analyzer 检测包依赖大小

  • UglifyJsPlugin 压缩过程中对碎片化的冗余代码(如 console 语句、注释等)进行自动化删除: (webpack4 默认使用)
  • require.ensure(vue-router 配置路由,使用 webpack 的 require.ensure 技术,也可以实现按需加载。)
  • 开启 Gzip (需要后端配合)
  • Tree-shaking、代码分割、移除用不上的依赖项等;
  • Webpack 优化

浏览器层面的 tcp 连接限制

  1. 如果处于 http1.1
    • 既然对同一域名做了限制,那么可以切分为多个域名,有的放图片,有的放视频,这不就可以突破限制了吗!这里还有一点好处,在原来未切分域名的时候,就算是请求一个小图标,主站的 cookie 也会被带上,十分浪费流量。
    • 减少连接数,我们也可以将一些体积较小的资源合并起来,这也就是雪碧图、资源内联等
  2. 升级 HTTP 2,从而实现单个 tcp 连接并行发送 http

图片优化

  • JPG 适用于呈现色彩丰富的图片,在我们日常开发中,JPG 图片经常作为大的背景图、轮播图或 Banner 图出现。

    复杂的、色彩层次丰富的图片,用 PNG 来处理的话,成本会比较高,我们一般会交给 JPG 去存储。

无损图像格式
渐进式图片加载

  • 图标使用 SVG
  • 雪碧图(CSS Sprites) 通过减少请求数优化

通过缓存优化

强缓存

通过 expires 设置过期时间(http1.1 后新增 Cache-Control 来替代)

**Cache-Control 属性:**no-store no-cache max-age public private(默认值。) (详情在HTTP 缓存

协商缓存

协商缓存机制下,浏览器需要向服务器去询问缓存的相关信息,进而判断是重新发起请求、下载完整的响应,还是从本地获取缓存的资源。

(304)如果服务端提示缓存资源未改动(Not Modified),资源会被重定向到浏览器缓存,

通过 last-modified 和服务器文件最后时间做对比

Etag 不单单指判断时间,还判断是否有变动,优先级也更高。当 Etag 和 Last-Modified 同时存在时,以 Etag 为准。

包括但不限于使用浏览器缓存、HTTP 缓存、后台缓存,比如使用 Service WorkerPWA 等技术

CDN 优化

将静态资源放到不同域名的 CDN 中

同一个域名下的请求会不分青红皂白地携带 Cookie,而静态资源往往并不需要 Cookie 携带什么认证信息。把静态资源和主页面置于不同的域名下,完美地避免了不必要的 Cookie 的出现!

同时也可以突破 一组{host,port}最多允计打开 6 个 HTTP 和 6 个 HTTPS 连接。

PRPL 模式

渲染阶段的优化

提前渲染

  • 使用服务端直出渲染,减少页面二次请求和渲染的耗时;SSR
  • 使用骨架屏
  • 使用秒看技术,通过预览的方式(比如图片)提前将页面内容提供给用户;

优化 DOM

总结起来有三种方式可以优化 HTML:缩小文件的尺寸(Minify)使用 gzip 压缩(Compress)使用缓存(HTTP Cache)

优化 CSSOM

和优化 DOM 策略一样,但是 CSS 还有一个可以影响性能的因素是:CSS 会阻塞关键渲染路径

CSS 资源的处理有几个特点:

  • CSS 下载时异步,不会阻塞浏览器构建 DOM 树
  • 但是会阻塞渲染,也就是在构建 render 时,会等到 css 下载解析完毕后才进行(这点与浏览器优化有关,防止 css 规则不断改变,避免了重复的构建)
  • 有例外, media query 声明的 CSS 是不会阻塞渲染的

媒体查询

通过改变媒体查询,让 css 不阻塞首屏渲染,当仍在当前页面生效

<link href="style.css"rel="stylesheet"media="print"onload="this.media='all'">

关于 CSS 的加载最佳实践:Critical CSS

Critical CSS 的意思是:把首屏渲染需要使用的 CSS 通过 style 标签内嵌到 head 标签中,其余 CSS 资源使用异步的方式非阻塞加载。

所以 Critical CSS 从两个方面解决了性能问题:

  1. 减少关键资源的数量(将所有与首屏渲染无关的 CSS 使用异步非阻塞加载)
  2. 减少关键路径的长度(将首屏渲染需要的 CSS 直接内嵌到 head 标签中,移除了网络请求的时间)。

避免使用@import

使用 link 标签 css 标签是并行下载

而使用 @import ,两个 CSS 变成了串行加载,前一个 CSS 加载完后再去下载使用 @import 导入的 CSS 资源。

所以避免使用 @import 是为了降低关键路径的长度。

遇到 JS 脚本资源

JS 脚本资源的处理有几个特点:

  • 阻塞浏览器的解析,也就是说发现一个外链脚本时,需等待脚本下载完成并执行后才会继续解析 HTML
  • 浏览器的优化,一般现代浏览器有优化,在脚本阻塞时,也会继续下载其它资源(当然有并发上限),但是虽然脚本可以并行下载,解析过程仍然是阻塞的,也就是说必须这个脚本执行完毕后才会接下来的解析,并行下载只是一种优化而已
  • JS 带 async 和 defer 的区别

遇到 img 图片类资源

遇到图片等资源时,直接就是异步下载,不会阻塞解析,下载完毕后直接用图片替换原有 src 的地方

通过合理使用浏览器 GPU 合成,提升浏览器渲染效率;

如何利用合成优化

离屏渲染

使用离屏渲染,在页面不可见的地方提前进行渲染(比如 Canvas 渲染)

动画帧率

通过将页面渲染帧率保持在 60FPS 左右,提升页面交互和渲染的流畅度。

比如一些 js 动画,用requestAnimationFrame

运行时优化

减少重绘和回流

我们每操作一次 DOM(不管是为了修改还是仅仅为了访问其值),都要过一次“桥”。

(栗子:

  1. 多次控制 DOM 情况,先用 js 进行处理,待计算完毕再提交给浏览器发出重计算请求
  2. 多个 style 更改,写进一个 class 进行 add)

重绘不一定导致回流,回流一定会导致重绘

平时用的比较多的就是动画脱离文档流,另外可以使用

提取组件的 CSS 到单独到文件

当使用单文件组件时,组件内的 CSS 会以 <style> 标签的方式通过 JavaScript 动态注入。这有一些小小的运行时开销,将所有组件的 CSS 提取到同一个文件可以避免这个问题,也会让 CSS 更好地进行压缩和缓存。

接口预请求

Vue 优化

React 性能优化

长任务拆分

通过将 Javscript 大任务进行拆解 + 并行计算的方式,有效地降低整体计算耗时,比如使用 Web Worker

使用Webassembly

其他

  • 算法或存储结构,提升计算效率
    • VSCode 使用红黑树优化文本缓冲区的计算;
  • 使用计算缓存,减少运算次数。
  • 将计算过程提前,减少计算等待时长
  • 避免复杂递归,导致的栈溢出
  • 避免存在全局泄露,比如全局变量、及时删除引用
  • 使用享元模式,减少对象创建,减少内存

总结说法

首先从 http 请求方面着手优化

一般都是两点,减少请求和减少包体积

比如图片来说,可以通过选择合适的图片格式,比如 jpg,png,webp 等小体积格式,图标尽量是用 SVG

另外我有通过写一个 node 小工具将图片再次通过 tinypng 再次压缩,

另外 如果公司资源足够的话,可以将静态资源放静态服务器部署 CDN,可以减少不必要的 cookie 携带和突破 HTTP 的请求限制,

像 js 等请求后置,避免阻塞关键路径渲染

另外就是从缓存着手,静态资源走强缓存,其他资源尽量走协商缓存,

包体积方面基本都是从 webpack 配置着手,比如进行配置 gzip、代码压缩,treeshark,去除 console 等等进行优化

另外从 DOM 上优化 对频繁触发事件进行防抖节流措施,减少 DOM 操作,使用文档碎片,动画脱离文档流,使用 RAF,优化无限滚动的虚拟列表,进行异步的视图更新

其次就是用户体验方面的优化,使用骨架屏和 SSR 渲染

另外就是框架方面的合理运用,异步等待的恰当使用。

参考资料

< 前端性能量化标准 >

优化案例