动态

详情 返回 返回

⚡️ [性能優化] 瀏覽器跨域帶來的一些性能問題 - 动态 详情

前言

字節 Web Infra 團隊最近開啓新一輪招聘了,如果你對 Web Framework/Runtime/Performance,或對編譯構建/Rust/AI 感興趣,可以來看看我們的崗位(具體崗位信息可以看這個招聘鏈接 👉 字節跳動 Web Infra - Web Solutions 團隊招人啦!)

簡歷可以發到我的郵箱 skychx@hotmail.com,我可以幫你內推崗位並跟進進度 🥳

<br/>

由於 Web 的開放特性,同樣是糊頁面,Web 前端工程師往往要和 CORS(跨域請求)做一些鬥爭,例如我之前遇到的一個 《SVG 圖片字體失效問題》 就是 CORS 引起的。

對 CORS 不太瞭解的同學,可以看我之前翻譯的這篇文章《15 張精美動圖全面講解 CORS》,圖文並茂,基本上可以對 CORS 有個大致的理解

CORS 除了會帶來一些資源加載失敗的問題,它其實還會對一些性能場景帶來一些干擾,本篇文章就是記錄一下工作中遇到的一些問題。因為水平有限,沒覆蓋到的地方歡迎大家評論補充。

<br/>

場景

1.預檢請求帶來的雙倍網絡通信

這個基本上算是最經典的跨域帶來的性能問題了。簡單來説就是在簡單請求(simple request)場景下,瀏覽器會先發一個預檢請求(preflight request),問問服務器支持什麼 HTTP Header,然後基於這個白名單決定是否要發起真正的網絡請求。

這部分內容建議看《15 張精美動圖全面講解 CORS》,結合動圖演示會有更好的理解

<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3d0ab9d9ae9449d592c23f54a2d86946~tplv-k3u1fbpfcp-image.image#?w=760&#x26;h=399&#x26;s=174387&#x26;e=svg&#x26;a=1&#x26;b=dee9c9" alt="Simple and Preflight Fetch">

這就導致網絡請求會多一次 HTTP OPTIONS 請求(即 preflight request),可能會帶來幾十到幾百毫秒的延遲,如果接口比較重要,可能會影響到首屏的加載時間。

<br/>

2.ResourceTiming API 數據丟失

<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ad585b6bd263427197176bb99c4bb91c~tplv-k3u1fbpfcp-image.image#?w=1152&#x26;h=464&#x26;s=180311&#x26;e=svg&#x26;a=1&#x26;b=cee2f2" alt="ResourceTiming">

一個網絡請求會經歷 DNS 尋址 + TLS 連接 + TCP 連接 + HTTP 請求響應等多個階段,這些請求細節的時間點位一般可以用 ResourceTiming API 來獲取:

performance.getEntriesByName('example.com/api')

獲取到的數據可以採集上報用來在後台大盤監控一些性能問題。但是在 CORS 場景下出於安全考慮,部分性能點位的數據會強制設置為 0,導致採集失效。


<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0682ebef17144565a89fbfb53344386b~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1438&#x26;h=906&#x26;s=82092&#x26;e=png&#x26;b=fefefe" alt="cross_origin vs same_origin" >

如上圖所示,如果資源的 HTTP Response Header 中的 Timing-Allow-Origin 不存在或未包含該站點的域名,就只會顯示 startTime/fetchStart/responseEnd 這個 3 個點位的真實數據(也就是隻顯示請求的 start 和 end,中間的細節全部不顯示),其它點位全部顯示 0。

<br/>

3.LCP 大於 FCP

先簡單介紹一下這兩個詞的含義:

  • FCP:First Contentful Paint,首次繪製時間,一般指瀏覽器繪製第一個像素的時間,這時候頁面往往還是白屏或者剛加載了骨架屏
  • LCP:Largest Contentful Paint,最大內容繪製時間,這時候頁面往往已經加載完成顯示出可見內容了


從概念上看,LCP 應該是大於或等於 FCP 的,但是在部分跨域場景下會出現 LCP 小於 FCP 的情況。這裏我們藉助 PerformanceElementTiming API 來理解這種場景。

對於一張圖片,它存在一個 loadTime,可以簡單理解為圖片資源下載完成的時間,這時候圖片還沒來得及顯示;還有一個 renderTime,這才是圖片真正顯示的時間。


如果存在這樣一種場景:

imageLoadTime < FCP < imageRenderTime

而在採集 LCP 數據時,正好是這張圖片觸發了 LCP,肯定要以 imageRenderTime 這個圖片顯示時間為準。但是如果這個圖片跨域,且沒有聲明 Timing-Allow-Origin,就會 fallback 到 imageLoadTime。這時候就會出現 LCP 小於 FCP 的情況。

這種情況一般還是比較少見的,但在實際業務中確實出現了這種情況,社區中也有類似的問題(eg: issues/260),所以也算一個 CORS 影響性能的案例。

<br/>

4.字體 preload 失效

瀏覽器 提供了 <link rel="preload" /> 標籤用來預加載資源,那麼字體資源也是可以預加載的。


首先説説字體預加載的必要性。對於一個自定義字體來説,並不是説我聲明瞭字體資源鏈接,就會發起字體的網絡請求:

@font-face {
  font-family: 'Quasari';
  src: url('https://fonts.cdnfonts.com/s/29552/Quasari.woff'); /* 並不會請求 Quasari.woff 資源 */
}
h1 {
  font-family: 'Quasari';
}

只有瀏覽器實際排版時,發現 DOM 裏有 h1 這個標籤,才會發起字體資源網絡請求,這就導致加載時機偏晚,可能會影響到 FCP/CLS 等性能指標,所以就有了 preload font 的必要性。


因為字體也有跨域問題,所以 preload 的時候,如果資源是跨域的,需要加上 corssorigin 屬性做跨域標識,這樣才能成功命中 preload 緩存,到這裏,其實都是符合預期的:

<link rel="preload" href="cors.com/font.woff" as="font" type="font/woff" crossorigin />


但是弔詭的是,如果字體資源不跨域,你也得加 corssorigin 屬性,否則緩存還是命中不了 😅:

<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d0072544ccce451c8af501fb3e5a7667~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=2224&#x26;h=1276&#x26;s=97751&#x26;e=png&#x26;b=ffffff" alt="same-origin-font-preload-fail" width="60%">

<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d048d9d4a3cc4d6da65d134f1c83a85d~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1954&#x26;h=1262&#x26;s=85806&#x26;e=png&#x26;b=ffffff" alt="same-origin-font-preload-success" width="60%">

<br/>

這個現象在 Chrome 上存在了快 10 年了,相關的討論可以看這個 issue,我也很難説這是個 bug 還是個 feature,總之到現在的 120 版本還是這樣的。但從我的角度看確實挺坑的,從 API 上看還是不太符合直覺。

<br/>

總結

瀏覽器作為 Web 的入口,安全問題一直是重中之重,最近幾年的一些新功能都會考慮到安全,例如:

  • 新的 API 功能必須在在 HTTPS 環境下才能調用
  • Cookie 的濫用問題一直在收口
  • 各種資源加載問題上會有各種 CORS/CSP 限制
  • ......

這些措施其實都是利於用户數據安全的,但是不可避免的會給開發者帶來 debug 和性能上的困擾,這個也是需要我們去長期跟進和學習的地方。

<br/>

參考

  • LCP: load\_time vs render\_time
  • Web Font Downloaded Twice When Preloading
  • Preload: What Is It Good For? -- early loading of fonts
  • w3c/preload: fonts and crossorigin
  • CSS Fonts Module Level 4

如果你喜歡我的文章,希望點贊👍 收藏 📁 評論 💬 三連支持一下,謝謝你,這對我真的很重要!

歡迎大家關注我的微信公眾號:滷蛋實驗室,目前專注前端技術,對圖形學也有一些微小研究。

原文鏈接: [⚡️ [性能優化] 瀏覽器跨域帶來的一些性能問題](https://supercodepower.com/cross-origin-and-performance):更新更及時,閲讀體驗更佳

user avatar cyzf 头像 Leesz 头像 alibabawenyujishu 头像 haoqidewukong 头像 smalike 头像 qingzhan 头像 kobe_fans_zxc 头像 dirackeeko 头像 aqiongbei 头像 razyliang 头像 leexiaohui1997 头像 inslog 头像
点赞 136 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.