我们有个非常经典的面试题,叫做《从用户按下回车到看到页面的过程中,都发生了什么》。
很多人都知道浏览器拿到 URL 后要解析域名,建立 TCP 连接、SSL 握手、等待后端响应等。当其中有个非常容易被忽略的环节: Redirect
。
只是一张比较常见的 Performance Model
的分析图,紧跟着 navigationStart
的第一步其实就是 redirect
。
更加精准的说,我们一般从监控层面统计到的 redirect
耗时是 fetchStart - navigationStart
的耗时。(后面会解释为什么不是 redirectEnd-redirectStart
)。
什么是 redirect
redirect
一般是指 HTTP 重定向,例如常见的 302/301
等。
一个常见的例子例如当我们访问 https://baidu.com
时就会发现连接自动变成了 https://www.baidu.com
,实际上是服务端响应 302
让浏览器重定向到了 https://www.baidu.com
。
而这个阶段实际上是非常耗时的,相比于直接访问 https://www.baidu.com
来说,相当于空走了一个发起请求 => 服务端响应
的时间。
我们可以通过 WebpageTest 来看一下这两者之间的区别。
访问 https://baidu.com
访问 https://www.baidu.com
可以看到前者经过 302 相当于整整多了一个请求,看似不经意的的一次跳转让整个访问时间几乎翻倍。
哪些情况下有 redirect
存在 redirect 的情况有很多种,比较常见的场景有:
- 登录鉴权后的跳转
- http => https 的升级重定向
- http://baidu.com => www.baidu.com 这种域名重定向,也常见于移动设备上 baidu.com => m.baidu.com
- 从 SEO 或者站外引流渠道来的流量,往往会从 Google/Facebook 等服务器 302 过来
- 投放短链的
其中一些场景是我们可控可优化的,例如把 baidu.com => www.baidu.com
这种重定向使用 301 而不是 302,从 http://alibaba.com => https://www.alibaba.com
的过程一次重定向完成而不是 http://alibaba.com => https://alibaba.com => https://www.alibaba.com
,以及在运营投放中尽可能避免短链等。
也有部分场景虽然在我们手中,但是没有什么优化空间的,例如登录鉴权本质上就需要请求一次后端后根据响应的结果进行跳转。
同时也有部分是我们完全不可控制的,例如从 Google 重定向过来的耗时。
比较典型的例如我们在 Gmail 中发送的连接
无论发送的是原连接还是文字链接,在 Gmail 的网页版中打开都会先去 www.google.com
的服务器,然后 302 到我们的链接上,这个过程就会造成相当长的耗时。
前端监控如何统计 redirect
先放结论,前端监控无法精确统计 redirect
耗时,我们只能统计到 fetchStart - navigationStart
的耗时。
原因是因为浏览器的安全策略规定了我们只能拿到同域的重定向信息,其中也包括性能信息。
而看上面常见的 redirect 的场景,几乎没有场景属于同域重定向。(http://baidu.com 和 https://baidu.com 也不属于同域)。于是大部分情况下,redirectStart
和 redirectEnd
都是空值。
为了方便讨论,我们把能够统计的这段不精准的 redirect
耗时命个名,叫 beforeFetch
。那么这段时间中还中还包括什么时间呢?
浏览器打开耗时
是的,如果用户从浏览器外(例如钉钉)打开一个连接,钉钉通知浏览器打开新标签页甚至是冷启动的时间都会被计算到 beforeFetch
中。
经过测试,从钉钉点开链接冷启动 Chrome,在 15 寸的 MacBook 上会导致页面的 redirect
耗时增加 700ms+
。
而在 Chrome 打开的情况下点开一个链接,也会增加 80ms
。
标签页初始化时间
同样的,初始化标签页时间也被计算在内(其实上面的 80ms 就是一种),只是平时感知不明显,如果你开着 Auto Devtools for popup
,在打开新标签页的情况下 beforeFetch
的时间也会增加的非常明显。
结论
- 减少可控部分的重定向,应该使用
301
的地方不要使用302
,尽量不投放短链或者需要不必要重定向的连接 - 固定的 302 逻辑(例如设备重定向到不同域名)可以前置到 CDN,减少耗时
beforeFetch
的时间在某些场景(例如邮件,推送等)场景确实会高很多,除了这些场景可能存在更多重定向外,也有可能是因为浏览器启动耗时也被统计进去了