前端人都应该知道的性能优化📕

前端性能优化 | 什么是性能优化 · 语雀

性能优化概述

性能优化的影响

  • 用户的留存 : 加载越快, 用户留存率越高

  • 网站的转化率 : 目标行为访问次数 / 总访问次数, 比如购物

  • 体验与传播 : 好评/差评

性能优化的评估模型

  1. 响应 : 网站对于响应方面的要求是:在用户感知延迟之前接收到操作的反馈 , 比如必须在 100ms 之内收到反馈

  2. 动画 : 要求流畅, 最好控制在 10ms 以内

  3. 空闲 : 利用空闲时间来处理可延迟的任务, 减少预加载的数据大小, 使用户感知不延迟

  4. : 先渲染关键渲染路径, 空闲时间再渲染非关键资源。

性能优化的指标

感官指标

  • 首次绘制时间 (FP) : 页面首次绘制的时间点

  • 首次有内容绘制时间 (FCP) : 浏览器完成渲染DOM第一个内容的时间点

  • 首次有意义绘制时间 (FMP) : 页面关键元素渲染时间

  • 首屏时间 : 进入页面之后, 应用渲染完整个屏幕(未滚动之前) 内容的时间.

Performance API

这个 API HTML5 新增的特性 , 不明白的可以看看 MDN

可以在控制台的console面板中通过window.performance来获取网站的各项指标信息

Performance 接口可以获取到当前页面中与性能相关的信息。它是 High Resolution Time API 的一部分,同时也融合了 Performance Timeline API、Navigation Timing API、 User Timing API 和 Resource Timing API。该类型的对象可以通过调用只读属性 Window.performance 来获得。

  • performance包括了五个属性,其中timing是需要重点关注的,timing是一个map数据结构,其中key值是性能优化指标,value值是对应的时间戳。
  • 很多指标都是成对出现,直接作差就可以求出对应页面加载过程中关键节点的时间,介绍几个比较常用的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const timingInfo = window.performance.timing; 

// TCP连接耗时
timingInfo.connectEnd - timingInfo.connectStart

// DNS查询耗时
timingInfo.domainLookupEnd - timingInfo.domainLookupStart;

// 获得首字节耗费时间,也叫TTFB
timingInfo.responseStart - timingInfo.navigationStart

// domReady时间(与前面提到的DomContentLoad事件对应)
timingInfo.domContentLoadedEventStart - timingInfo.navigationStart

// DOM资源下载
timingInfo.responseEnd - timingInfo.responseStart

上述就是比较常用的指标,这些指标也可以在Chrome浏览器的network面板中的Timing下获取:

性能优化的步骤

  • 传输资源的优化:比如图像资源,不同的格式类型会有不同的使用场景,在使用过程中判断是否恰当;
  • 加载过程的优化:比如加载延迟,是否有不需要在首屏展示的非关键信息,占用了页面的加载时间;
  • JavaScript的优化:JavaScript代码是否进行了压缩,书写是否规范,有无考虑内存泄漏等;
  • 关键渲染路径优化:比如是否存在不必要的回流与重绘等;
  • 本地存储和浏览器缓存。

前端页面的性能检测

常见的性能检测工具

  1. Chrome 任务管理器 : 查看当前所有进程关于GPU、网络、和内存的使用情况, 这些进程包括当前打开的标签页、安装的各种插件以及GPU、网络、渲染等浏览器的默认进程。通过监控这些数据, 可以定位存在内存泄漏或者网络资源加载异常的问题进程。

很熟悉了

  1. Network 面板 : 可以查看网站资源的请求情况,包括加载时间、尺寸大小、优先级设置、HTTP缓存触发情况等信息,从而发现网站资源请求中存在的问题。

很熟悉了

  1. Coverage 面板 : 监控并统计出网站应用运行过程中代码执行的覆盖率情况。该面板统计的对象是JavaScript脚本文件与CSS样式表文件。代码覆盖率低就意味着该代码文件存在着较多没有用到的代码,开发者可以根据这个结果对代码进行拆分,在需要的时候在进行加载。

    img

点击’更多工具’打开

  1. PageSpeed : https://pagespeed.web.dev/ 可以直接输入页面的URL来分析页面的性能

img

  1. WEBPAGETEST : 可以对检测分析的环境配置进行自定义化,内容包括测试节点的物理位置、设备型号、浏览器版本、网络条件和检测次数等。https://www.webpagetest.org/

Performance

Performance(性能)面板主要是对网站应用的运行时性能表现进行检测与分析,其可以检测的内容包括:页面的每秒帧数(FPS)、CPU的使用情况、各种请求的时间花费、网络任务的执行情况。建议在无痕模式下使用该工具,因为该模式下网页不会受到缓存或其他插件程序等因素的影响

img

控制面板

  • 屏幕截图:表示是否截取每一帧的屏幕截图,默认会勾选

  • 内存:表示是否记录内存消耗,默认不会候选

  • Web Vitals:测试网页的加载速度及用户体验等

  • Disable javaScript samples:关闭JavaScript样本,减少在手机端运行时的开销

  • Enable advanced paint instrucmentation(slow):表示是否开启加速渲染工具,用来记录渲染事件的相关细节,该功能比较消耗性能

  • Network:切换模拟网络环境,可以模拟弱网(2g/3g)条件下网站的一些表现情况,然后根据弱网的具体表现进行相关优化

  • CPU:限制CPU的处理速度,主要用于模拟低速CPU运行时的性能。

概览面板

在概览面板中,可以选择一个起始时间点,然后拖动鼠标左键来滑动选择面板中国的局部范围,来进行更小范围的性能观察。这部分的性能信息主要包括以下三个指标:

  • FPS:衡量动画是否流畅的重要指标。

  • CPU:展示CPU的使用情况。

  • NET:展示各个请求所花费的具体时间

线程面板

主线程在解析HTML和CSS、页面绘制及执行JavaScript的过程中,每个事件调用栈和耗时情况都会反应在这个图中。

每一个长条都代表一个事件,将鼠标悬浮至其上面时,就可以查看到相应的时间的执行耗时与事件名。

图中不同颜色表示不同的事件类型。横轴代表时间,纵轴代表具体的调用堆栈。调用堆栈就像是浏览器当中的解释器,可以利用它追踪函数的执行流。

常见的事件类型有:

  • HTML解析;
  • JavaScript事件;
  • 页面布局更改;
  • 元素样式重新计算;
  • 页面涂层的绘制。

统计面板

统计面板会根据在概念面板中选择时间区域的不同,绘制出不同类型任务执行耗时的可视化图标

点击之后看到这个事件下对应的一些具体信息,这里还可以点击Range右侧链接,点击之后会直接跳到sources面板对应的代码位置,这个功能方便了定位代码

img

img

统计面板中包含四个Tab,其含义如下:

  • Summary:展示各类任务事件耗时环形图;
  • Bottom-Up:查看各个事件耗时的排序列表,列表包含两个维度:去除子事件后该事件本身的耗时和包含子事件从开始到结束的总耗时;
  • Call Tree:查看全部或指定火焰图中某个事件的调用栈;
  • Event Log:查看关于每个事件详细的日志。

LightHouse

为 Lighthouse 提供一个需要审查的网址,它将针对此页面运行一连串的测试,然后生成一个有关页面性能的报告,其内容包括:性能检测、可访问性检测、SEO检测、是否符合PWA的检测、其他是否符合最佳实践的检测。 检测报告不仅涉及上面这些方面的现状分析,同时还提供了一些优化指导建议,方便开发者快速发现潜在的性能瓶颈并实施优化改进。

在新版的Chrome浏览器中,将LightHouse集成在了开发者工具中,打开控制台,切换到LightHouse面板

可以看到报告详细给出了各种情况下的分数情况,从左到右分别是Performance(页面性能)、Accessibility(可访性)、Best Practise(最佳实践)、SEO(搜索引擎优化)、Progressive Web App(渐进式应用)。点击其中的每一项都可以看到给出的具体优化建议。

检测得分

img

页面性能

img

优化建议

img

根据每部分给出的评测结果以及优化建议,开发者就可以针对性的对网站页面进行优化。

雅虎35条军规

可能没有35条, 因为有些已经不需要了

1. 图片优化

优化图片

  • 检查 GIF 图片的调色板大小是否匹配图片颜色数;

  • 把 GIF 格式转换成 PNG 格式,看看是否节省空间;

  • 运行 pngcrush 或其它工具来压缩 png 格式的图片;

  • 运行 jpegtran 或其它工具来压缩 jpeg 格式的图片。

优化CSS Sprite

  • 把图片横向合并而不是纵向,因为横向图片文件更小;

  • 把颜色近似的图片合并到一张雪碧图,这样可以让颜色数更少,如果低于 256 色就可以用 png8 格式;

  • 对移动端友好,合并时图片间的间距不要太大。虽然这对图片大小影响不是太大,但这样做可以节省用户代理把图片解压成像素映射时消耗的内存。100×100的图片是1万个像素,而1000×1000的图片就是100万个像素了。

禁止在HTML中缩放图片

不要在 HTML 中缩放图片。不要因为可以设置图片的宽高就去用比需要的大得多的图片。

如果需要100px * 100px的图片,那就不要用500px * 500px的。

用小的且可以缓存的favicon

  • favicon.ico是放在服务器根目录的图片,浏览器也会自动请求它,所以最好不要给一个404 Not Found响应。而且只要在同一个服务器上,每次请求它时都会发送cookie,此外这个图片还会干扰下载顺序,例如在IE中,当你在onload中请求额外组件时,将会先下载favicon

  • 所以为了缓解favicon.ico的缺点,应该确保:

    • 足够小,最好在1K以下;

    • favicon.ico 一般是不进行更换的,所以可以给它设置Expires头,而且可以安全地设置为几个月,避免每一次打开页面都需要去进行请求。

2. CSS 优化

将CSS样式放在顶部

  • 将样式表移到 <head> 里会让页面加载地更快。这是因为把样式表移到 <head> 里允许页面逐步渲染。

  • 我们希望浏览器尽早的去渲染获取到的内容,这对大页面和网速慢的用户很重要。给用户视觉反馈(比如进度指标)就非常重要,HTML 页面就是进度指标。当浏览器逐步加载页面头部,导航条,顶部 logo 等内容时,这些都是给等待页面的用户的视觉反馈,能够提高整体用户体验。

  • 把样式表放在文档底部的问题是它阻止了许多浏览器的逐步渲染,包括 IE。这些浏览器阻止渲染来避免在样式更改时需要重绘页面元素。所以用户会卡在白屏。

避免使用CSS表达式

CSS 表达式是强大的(可能也是危险的)设置动态 CSS 属性的方法。CSS 表达式的问题是它们可能比预期计算的更频繁。它们不仅在页面载入和调整大小时重新计算,也在滚动页面甚至是用户在页面上移动鼠标时计算。比如在页面上移动鼠标可能轻易计算超过10000次。要避免CSS表达式计算太多次,可以在它第一次计算后替换成确切值,或者用事件处理函数而不是CSS表达式。

选择<link> 舍弃@import

上面提到了一个最佳实践:为了实现逐步渲染,CSS应该放在顶部。在IE中用@import与在底部用<link>效果一样,所以最好不要用它。

减少Cookie的体积

HTTP Cookie 的使用有多种原因,比如授权和个性化。Cookie 的信息通过 http 头部在浏览器和服务器之间交换。

重要的是保证cookie尽可能的小,以最小化对用户响应时间的影响。可以对Cookie做如下优化:

  • 消除不必要的 Cookie , 来降低响应时间;
  • 注意设置 Cookie 到合适的域名级别,以免影响其它子域;
  • 设置合适的 Expires 日期。更早的有效期或者none可以更快的删除cookie,提高用户响应时间。

把组件放在不含Cookie的域下

当浏览器请求静态图片并把 cookie 一起发送到服务器时,cookie 此时对服务器没什么用处。所以这些 cookie 只会增加无意义的网络流量。所以应该保证静态组件的请求是没有 cookie 的。可以创建一个子域名来托管所有静态组件。

比如,域名是www.example.org,可以把静态组件托管在static.example.org。不过,如果把cookie设置在顶级域名 example.org 下,这些cookie仍然会被传给 static.example.org。这种情况下,可以启用一个全新的域名来托管静态组件。

注意:因为cookie是可以跨二级域名的,所以如果设置的顶级域名是example.org,那么static.example.org 也是可以被访问到的,因此需要启用一个全新的域名。

另外一个用没有 cookie 的域名提供组件的好处是,某些代理可能会阻止缓存带 cookie 的静态组件请求。

4. 服务端优化

使用CDN(内容分发网络)

  • 用户接近服务器就会减少响应时间。把内容发布到多个地理上分散的服务器可以让页面加载更快。
  • 80-90%的终端用户响应时间花费在下载页面中的所有组件:图片、样式、脚本、falsh 等,这就是业绩黄金法则。
  • 最好先分散静态内容,而不是一开始就重新设计应用程序结构。这不仅能够大大减少响应时间,还更容易表现出CDN的功劳。
  • 内容分发网络(CDN)是一组分散在不同地理位置的web服务器,用来给用户更高效地发送内容。典型地,选择用来发送内容的服务器是基于网络距离的衡量标准的。

添加Expires或者Cache-Control头部

这条规则有两个方面:

  • 对静态组件:通过设置 Expires头部来实现永不过期策略。
  • 对动态组件:用合适的 Cache-Control 头部来帮助浏览器进行有条件性的请求。

网站设计越来越丰富,这意味着更多脚本,样式,图片等。第一次访问的用户可能需要发出多个请求,但使用Expires可以让这些组件被缓存。这避免了访问子页面时没必要的 http 请求。Expires 一般用在图片上,但它应该用在所有的组件上,包括脚本、样式等。

浏览器使用缓存来减少HTTP请求数和大小,加快页面加载。服务器使用HTTP响应的Expires头部来告诉客户端一个组件可以缓存多久。注意,如果设置了Expires头部,当组件更新后,必须更改文件名。

传输时用gzip等压缩组件

HTTP 请求或响应的传输时间可以被显著减少。压缩可以通过减少 http 响应的大小来缩短响应时间。从 HTTP/1.1 开始,客户端通过http请求中的 Accept-Encoding 头部来提示支持的压缩:

1
Accept-Encoding: gzip, deflate

如果服务器看到这个头部,它就会选用列表中的某个方法压缩响应。服务器通过Content-Encoding 头部来提示客户端:

1
Content-Encoding: gzip

gzip 一般可减小响应的 70%。尽可能去gzip更多(文本)类型的文件,这也是提升用户体验最简单的方法。

html,脚本,样式,xml 和 json 等都应该被gzip,而图片,pdf等不应该被gzip,因为它们本身已被压缩过,gzip 它们只是浪费 cpu,甚至增加文件大小。

配置 ETags

协商缓存

实体标记(Entity tags,简称ETag)是服务器和浏览器之间判断浏览器缓存中某个组件是否匹配服务器端原组件的一种机制。实体就是组件:图片,脚本,样式等。ETag被当作验证实体的比最后更改(last-modified)日期更高效的机制。一个ETag是一个字符串,作为一个组件某一具体版本的唯一标识符。唯一的格式约束是字符串必须用引号括起来。服务器这样设置组件的ETag:

1
2
3
4
HTTP/1.1 200 OK
Last-Modified: Tue, 12 Dec 2006 03:03:59 GMT
ETag: "10c24bc-4ab-457e1c1f"
Content-Length: 12195

然后,如果浏览器要验证组件,它用 If-None-Match 头部来把 ETag 传回服务器。如果 ETag 匹配成功,服务器返回状态码304:

1
2
3
4
5
GET /i/yahoo.gif HTTP/1.1
Host: us.yimg.com
If-Modified-Since: Tue, 12 Dec 2006 03:03:59 GMT
If-None-Match: "10c24bc-4ab-457e1c1f"
HTTP/1.1 304 Not Modified

ETag 的问题是它们被构造来使它们对特定的运行这个网站的唯一服务器。

浏览器从一个服务器获取组件,之后向另一个服务器验证,ETag 将不匹配。

然而服务器集群是处理请求的通用解决方案。如果不能解决多服务器间的 ETag 匹配问题,那么删除 ETag 可能更好。

尽早清空缓冲区

当用户请求一个页面,服务器一般要花 200-500ms 来渲染整个页面。这段时间,浏览器是空闲的(等待数据返回)。在 php中,有个方法 flush() 允许传输部分准备好的 html 响应给浏览器。这样的话浏览器就可以开始下载组件,而同时后台可以继续生成页面剩下的部分。这种好处更多是在忙碌的后台或轻前端网站可以看到。

较理想的清空缓冲区的位置是HEAD后面,因为HTMLHEAD部分通常更容易生成,并且允许引入任何CSSJavaScript文件,这样就可以让浏览器在后台还在处理的时候就开始并行获取组件。

对Ajax用GET请求

当使用 XMLHttpRequest 时,POST请求被浏览器实现为两步:首先发送头部,然后发送数据。所以最好使用 GET,仅用一个 TCP 包发送(除非cookie太多)。如果POST不提交任何数据,那它跟 GET 行为类似。但从语义上讲,获取数据应该用 GET,提交数据到服务器用 POST

避免图片src属性为空

src属性的图片的行为有两种形式:

1
2
<img src="">  // html标签:
var img = new Image(); img.src = "" // js
  • 上面的两种形式都会造成同一种后果:浏览器会向服务器发送另一个请求。
  • 发送大量的意料之外的流量,会削弱服务器,甚至可能会破坏用户数据。如果你在跟踪请求状态,通过 cookie 或其它,可能会破坏数据。即使 image 的请求不会返回图片,但所有的头部数据都被浏览器读取了,包括 cookie。即使剩下的响应体被丢弃,破坏可能已经发生。 这种行为的根源是 uri 解析发生在浏览器。
  • RFC 3986 定义了这种行为,空字符串被当作相对路径,Firefox, Safari, 和 Chrome都正确解析。总之,浏览器解析空字符串为相对路径的行为被认为是符合预期的。
  • html5在4.8.2添加了对标签src属性的描述,指导浏览器不要发出额外的请求。幸运的是将来浏览器不会有这个问题了(仅在图片上)。不幸的是,<script src=""><link href="">没有这样的规范。

5. JavaScript 优化

把脚本放到底部

  • 脚本会阻塞并行下载。HTTP/1.1 规范建议浏览器每个域名下不要并行下载超过2个组件。如果图片分散在不同服务器,那么就能并行下载多个图片。如果脚本正在下载,浏览器就不开始任何其它下载任务,即使是在不同主机名下的。
  • 有些情况下,把脚本移动到底部并不简单。比如,脚本中用了 document.write 来插入内容,它就不能被移动到底部。另外有可能有作用域问题。但大多数情况,这些问题都有办法解决。
  • 一个替代建议是使用异步脚本。defer/async属性表明脚本不包含 document.write,并且提示浏览器可以继续渲染。如果脚本能异步加载,那么也就可以把它移动到底部,这样可以大大加快网页运行速度。
  • 这里要注意JavaScript是会阻塞浏览器运行的,所以脚本文件尽量放到页面的最下面。

使用外部JavaScript和CSS

  • 实际上,使用外部文件一般可以让页面加载更快,因为 JS 和 CSS 文件会被浏览器缓存。内而联的 JavaScript 和CSS 在每次 HTML 文档下载时都被下载。虽然内联减少了http请求,但增加了HTML文档大小。另一方面,如果 JavaScript CSS 被缓存了,那么 HTML 文档可以减小大小而不增加 HTTP 请求。
  • 核心因素就是 JavaScript CSS 被缓存相对于 HTML 文档被请求的频率。如果网站用户每个会话打开了多个页面,许多页面重复使用相同的 JavaScript 和CSS,那么有很大可能用外部 JS 和 CSS 更好。
  • 许多网站用这些指标计算后在中间位置。对这些网站来说,最佳方案还是用外部 JS 和 CSS 文件。唯一例外是内联更被主页偏爱。主页每个会话可能只会打开少量甚至一个页面,这时候内联可能更快。

根据实际业务需求进行选择

压缩JS和CSS

  • 压缩就是删除代码中不必要的字符来减小文件大小,从而提高加载速度。当代码压缩时,注释删除,不需要的空格(空白,换行,tab)也被删除。在JavaScript中这样做能够提高响应性能,因为要下载的文件变小了。两个最常用的JavaScript代码压缩工具是JSMinYUI CompressorYUI compressor还可以压缩CSS
  • 混淆是对代码可选的优化。它比压缩更复杂,并且可能产生 bug。在对美国 Top10 网站的调查中,压缩可减小 21%,而混淆可以减小 25%。除了外部脚本和样式,内联的脚本和样式同样应该被压缩。
  • 除了压缩外部脚本和样式,行内的<script><style>块也可以压缩。即使启用了gzip模块,先进行压缩也能够缩小5%或者更多的大小。JavaScriptCSS的用处越来越多,所以压缩代码会有不错的效果。

删除重复的脚本

  • 在页面中两次引入相同的脚本会降低性能。当确实引入重复脚本,会发出不必要的http请求和浪费js执行时间。
  • 除了产生没有意义的HTTP请求之外,多次对脚本求值也会浪费时间, 都会执行冗余的JavaScript代码。
  • 避免不小心把相同脚本引入两次的一种方法就是在模版系统中实现脚本管理模块。

尽量减少DOM访问

用 JavaScript 访问 DOM 元素是很慢的,所以为了让页面反应更迅速,应该:

  • 缓存访问过的元素的引用;
  • 在 DOM 树外更新节点,然后添加到 DOM 树;
  • 避免用JavaScript修复布局问题;

注意,能用CSS解决的事情,就尽量不用JS操作DOM,DOM操作开销很大。

用智能的事件处理器

有时候页面看起来响应速度比较慢,是因为绑定到不同元素的大量事件处理函数执行太多次。一种更好的解决方法就是使用事件委托。另外,不必等到 onload 事件来开始处理 DOM 树,使用DOMContentLoaded 会更快。大多时候需要的只是想访问的元素已在 DOM 树中,所以不必等到所有图片下载完。

注意:

  • onLoad是的在页面所有文件加载完成后执行;
  • DomContentLoadDom加载完成后执行,不必等待样式脚本和图片加载。

6. 移动端优化

保持组件小于25K

这个限制是因为iPhone不能缓存大于25K的组件,注意这里指的是未压缩的大小。这就是为什么缩减内容本身也很重要,因为单纯的gzip可能不够。

组件打包到一个复合文档中

将组件打包到复合文档就像带有附件的电子邮件,它可以帮助我们通过一个 HTTP 请求获取多个组件(请记住:HTTP 请求很昂贵)。使用此技术时,首先检查用户代理是否支持它( iPhone 就不支持)。

7. 内容优化

减少HTTP请求数

80%的终端用户响应时间都花在了前端上,大部分用于下载组件 js/css/image/flash 等。减少组件数必然能够减少渲染页面所需的 http 请求数。这是让页面更快的关键。

减少组件数的一个方法就是简化页面设计。保持富内容的页面且能减少 http 请求,有以下几个技术:

  • Combined files (合并文件):通过把所有脚本放在一个文件中的方式来减少请求数的,当然,也可以合并所有的CSS。如果各个页面的脚本和样式不一样的话,合并文件就是一项比较麻烦的工作了,但把这个作为站点发布过程的一部分确实可以提高响应时间。
  • CSS Sprites(雪碧图):雪碧图可以合并多个背景图片,通过 background-imagebackground-position 来显示不同部分。
  • Image maps (图片映射):可以把多张图片合并成单张图片,总大小是一样的,但减少了请求数并加速了页面加载。图片映射只有在图像在页面中连续的时候才有用,比如导航条。给image map设置坐标的过程既无聊又容易出错,用image map来做导航也不容易,所以不推荐用这种方式。
  • Inline images (内联图片):使用 data:url scheme 来内联图片,将内嵌图像组合到(缓存的)样式表中同样也是一种减少 HTTP 请求并避免增加页面大小的方法。Inline images 这里是将矢量图标转换为base64编码,然后直接内嵌到HTML文件或者CSS文件当中,减少http请求。

减少请求数是为第一次访问页面的用户提高性能的最重要的指导。

减少DNS查询

在浏览器地址栏输入网址,通过 DNS 查询得到网站真实 IP。DNS 查询被缓存来就可以提高性能。这种缓存可能发生在特定的缓存服务器(ISP/local area network维护),或者用户的计算机。DNS 信息留存在操作系统 DNS 缓存中。大多浏览器有自己的缓存,独立于操作系统缓存。只要浏览器在自己的缓存里有某条DNS 记录,它就不会向操作系统发 DNS 解析请求。IE默认缓存 DNS 记录30分钟,FireFox 默认缓存1分钟。

当客户端的 DNS 缓存是空的,DNS 查找次数等于页面中的唯一域名数。减少DNS请求数可能会减少并行下载数。避免 DNS 查找减少响应时间,但减少并行下载数可能会增加响应时间。指导原则是组件可以分散在至少2个但不多于4个的不同域名。这是这两者的一个平衡点。

避免重定向

重定向用301或302状态码来完成。

一个301响应 http 头的例子:

1
2
3
HTTP/1.1 301 Moved Permanently
Location: http://example.com/newuri
Content-Type: text/html
  • 浏览器自动跳转到 Location 指定的路径。重定向所需的所有信息都在 http 头部,所以 http主体一般是空的。301和302响应一般不会被缓存,除非有额外的头部信息,比如 Expires 或 Cache-Control 指定要缓存。meta 刷新标签或 JavaScript 也可以跳转,但如果真要跳转,3xx跳转更好,主要是保证返回键可用。
  • 最重要的是重定向会降低用户体验。在用户和 HTML 文档之间插入重定向会延迟页面中的所有内容,因为页面中的任何内容都无法呈现,并且在 HTML 文档到达之前不会开始下载任何组件。
  • 最浪费的跳转之一发生在url尾部斜杠(/)缺失。比如http://astrology.yahoo.com/astrology会301跳转到http://astrology.yahoo.com/astrology/。这可以被Apache等服务器修复,如果使用的是Apache处理程序,则可以使用Alias,mod_rewrite或DirectorySlash指令来取消不必要的重定向。
  • 重定向最常见的用途是把旧站点连接到新的站点,还可以连接同一站点的不同部分,针对用户的不同情况(浏览器类型,用户帐号类型等)做一些处理。用重定向来连接两个网站是最简单的,只需要少量的额外代码。虽然在这些时候使用重定向减少了开发人员的开发复杂度,但降低了用户体验。一种替代方案是用Alias和mod_rewrite,前提是两个代码路径都在相同的服务器上。如果是因为域名变化而使用了重定向,就可以创建一条CNAME(创建一个指向另一个域名的DNS记录作为别名)结合Alias或者mod_rewrite指令。

让Ajax可缓存

使用 ajax 的好处是可以向用户提供很快的反馈,因为它是向后台异步请求数据。但是,这些异步请求不保证用户等待的时间——异步不意味着瞬时。提高ajax性能的最重要的方法是让响应被缓存,即在上面讨论的 Expires 。其它方法是:

  • gzip 组件
  • 减少 DNS 查找
  • 压缩 JS
  • 避免跳转
  • 设置 ETags

延迟加载组件

我们需要考虑什么是页面初始化时所必须的。剩下的内容和组件就可以延迟加载。JavaScript 是理想的(延迟)候选者,可以切分到 onload 事件之前和之后。比如拖放的 js库可以延迟,因为拖动必须在页面初始化之后。其它可延迟的包括隐藏的内容,折叠起来的图片等。

预加载组件

预加载看起来与延迟加载是相反的,但它的确有不同的目标。通过预加载可以利用浏览器的空闲时间来请求将来会用到的组件。这样当用户访问下一个页面时,会有更多的组件已经在缓存中,这样会极大加快页面加载。预加载类型:

  • 无条件预加载:一旦 onload 触发,你立即获取另外的组件。比如谷歌会在主页这样加载搜索结果页面用到的雪碧图。
  • 有条件预加载:基于用户动作,推测用户下一步会去哪里并加载相应组件。
  • 预期的预加载:在发布重新设计的网站前提前加载。在旧网页预加载新网页的部分组件,那么切换到新网页时就不会是没有任何缓存了。

减少DOM元素的数量

一个复杂的页面意味着更多的内容需要下载,以及更慢的 DOM 访问。比如在有 500个DOM 元素的页面添加事件处理就和有 5000 个DOM 元素是有区别的。

如果页面 DOM 元素很多,那么意味着可能需要删除无用的内容和标签来优化。

把组件分散到不同的域名

把组件分散到不同的域名允许你最大化并行下载数。但要确保只用不超过2-4个域,因为存在DNS查找的代价。例如,可以把HTML和动态内容部署在www.example.org,而把静态组件分离到[static1.example.org]()和[static2.example.org]()。

尽量少用 iframe

iframe 允许 html 文档被插入到父文档里。

iframe的优点

  • 帮助解决缓慢的第三方内容的加载,如广告和标志;

  • 并行下载脚本;

  • 安全沙箱;

iframe的缺点

  • 即使空的也消耗(资源和时间);
  • 阻塞了页面的onload;
  • 非语义化(标签);

杜绝404

http 请求是昂贵的,所以发出 http 请求但获得没用的响应(如404)是完全不必要的,并且会降低用户体验。一些网站会有特别的 404 页面提高用户体验,但这仍然会浪费服务器资源。最坏的情况的是当链接指向外部 js但却得到 404 结果,这样首先会占用并行下载数,其次浏览器可能会把 404 响应体当作 js来解析,试图从里面找出可用的东西。


前端人都应该知道的性能优化📕
http://example.com/2023/11/09/性能检测与优化/
作者
weirdo
发布于
2023年11月9日
更新于
2023年11月9日
许可协议