[譯] 構建世界上最快的會議網站

徐二斤發表於2019-04-22

這是 JSConf EU 的組織者 Malte Ubl 的客座文章。

是不是被標題誘惑來啦,但我們絕對不會讓你白來一趟的!我不確定這一定是世界上最快的會議網站,但我也不確定它不是;而且我花了一大筆多到不合理的時間試圖讓它成為世界上最快的會議網站。我也是網路元件庫 AMP 的建立者,它可以用於搭建快速可靠的網站,同樣,這些網站也是我嘗試新技術進行優化的遊樂場,然後我可以將它們應用到日常工作中。此外,快速網站有更好的轉換率,在我們的情況下這意味著:賣出更多的門票

這個 JSConf EU 網站是搭建在靜態網頁生成器 wintersmith 上的。如果你知道 Jekyll 是什麼,那你一定也會知道什麼是 wintersmith。基本上都差不多,它們都基於 Node.js。Wintersmith 還好,預設情況下它不會做什麼可怕的事情,但是有一些我需要的東西必須自己構建。

字型

內聯

OMG,我花了好多時間來優化字型效能。你知道怎麼擁有比 JSConf 網站更快的字型效能嗎?那就去使用系統字型吧,但那樣會有些無聊。我們使用了 Typekit 的字型,它的字型都很贊。Typekit 要求你載入一個 CSS 檔案或者 JS 檔案,用來告訴網站字型檔案在哪裡。這對效能來說太可怕了:載入檔案意味著等待網路,而網路速度很慢。由於 DNS 解析,TCP 和 TLS 連線等原因,新增一個指向第三方主機的 CSS 檔案到頁面可以輕易地影響首屏渲染時間,可能會有大概 600 ms。我們修復了這個問題,方法是在構建過程中下載 CSS 檔案,然後在 CSS 中內聯它們。問題解決了,我們贏得了 600 ms。

事實證明 Typekit CSS 檔案實際上使用了 @import 來新增其他的 CSS 檔案。我不確定這個會不會阻塞渲染,但這個肯定不好。原來該檔案是空的,對它的請求僅僅用於統計資訊的收集。為了避免這種情況,我在編寫的指令碼檔案中移除了內聯 CSS 中的 @import哈哈,正則),然後在 JavaScript 中儲存這個請求的連結,頁面載入完畢(不會再影響首評渲染時間)之後,再去請求。

字型顯示

好了,既然我們已經內聯了 Typekit 的 CSS,我們也可以通過更多的正規表示式輕鬆地改變它。在 @font-face 規則中新增 font-display: fallback,可以阻止字型繪製時對下載的阻塞,基於上面的原因我們在指令碼中新增了這樣的程式碼。

不可變的 URL

我希望 wintersmith 能有一個真正的資源管道(asset pipeline)。它確實有資源處理程式,但每種資源只有一個。所以,在沒有程式碼重複的情況下,你無法輕鬆地為 CSS 檔案和 SVG 執行操作。但誰關心會議網站的程式碼重複?

黑入了 wintersmith,將雜湊值插入到所有本地可用的資源 URL 中,併為它們提供了一個在 CDN 中配置的公共路徑字首,用來有效地發出無限的快取頭。同樣,我們的 ServiceWorker 知道它永遠不必擔心這些 URL 過期並且可以永久保留資源。如果你複製我們的程式碼,請考慮縮短雜湊值。對於我們這個用例,在現實世界中沒有人需要這個完整的 SHA-XXX 串,而且還能在 HTML 中省下相當多的位元組。我現在不打算改變它,因為這會破壞所有資源的快取。

CSS

死碼消除

JSConf EU 網站使用 Tachyons 作為 CSS 框架。Tachyons 很不錯,除了它的體積很大,而且就算是最小的基礎包中也具有所有的功能。我安裝了一個 CSS 死碼消除(DCE)後處理步驟,它可以檢視所有靜態網站生成器生成的實際標記,然後將從不匹配任何內容的 CSS 選擇器都修剪掉。在我們的例子中,它將 CSS 體積減少了超酷的 85%。

內聯

既然 CSS 的體積非常小,那麼將它內聯到每個頁面中就說的過去了。你可能會想“但是快取怎麼辦?”很好的問題,但是如果你想在這場關於最快網站的競賽中獲勝,你就無法負擔得起冷快取狀態下額外的請求。所以我內聯了 CSS,而且事實上我們可以在 CSS DCE 上做的更好。所以我再次在每個頁面上執行它,對於每個頁面又節省了額外的 15-25% 的 CSS 體積。

順便說一句:先在整個網站執行一次 CSS DCE,然後再在每一頁上進行一次該處理是非常有意義的。這是因為對於整個網站來說 DCE 處理的時間複雜度是 O(所有 HTML 大小 + CSS 大小) ,對於單個頁面是 O(頁面數量 *(平均 HTML 大小 + CSS 大小)。如果你首先在整個網站上執行優化,CSS 體積的減少可以讓接下來對於單個頁面的優化明顯更快 —— 至少如果你可以在首次處理時減少 85% 的體積,就像我們的例子中實現的那樣。

內容管理

大多數靜態網站生成器都希望通過將 markdown 檔案放入 git 中來管理網站。JSConf EU 網站使用 Google Spreadsheet 來維護結構化資料(例如演講者資料)和 Google Docs 來儲存像這樣的部落格文章。雖然這不會讓網站執行更快,但它確實使編輯速度更快,所以它仍然可以計入最快的會議網站。例如說這個,就是你現在看到的這篇文章!

作為 Google GSuite 後端(LOL)構建過程的一部分,我們應該進行影象優化。不幸的是,還沒有足夠的時間來製作 webp 影象,所以如果你想建立一個更快的會議網站,這肯定是個突破口!

ServiceWorker

JSConf 網站有一個基於 Workbox 的 ServiceWorker。ServiceWorker 並不總有益於效能。它們必須安裝註冊,然後通常還得額外的配置 IndexedDB。這可能花費 100 毫秒。然而,對於會議網站而言,離線功能在效能問題上肯定會勝過糟糕的會議 Wi-Fi(我們的會議 Wi-Fi 通常很出色,但會議室有 1400 人,我們希望做好準備)。通過使用 導航預載入來進一步緩解這個問題,在大部分瀏覽器中導航預載入可以分攤文件網路請求的啟動時間。

為了權衡新鮮程度以及離線功能,該網站使用了“網路優先”策略,我們會首先嚐試獲取新的文件,如果在 2 秒之內沒有響應,我們會回退到快取。

因為網站的所有資源都使用了不可變的 URL,ServiceWorker 將永久快取這些 URL 並始終從快取(如果可用)提供服務。

動畫

你可能已經注意到在我們的首頁上有一個很大的動畫 X。很顯然,如果沒有這個動畫,頁面的載入速度會更快,但是那樣的話還有什麼樂趣呢?這個動畫是基於 Lottie-Web 庫製作的,這是一個由 AirBnB 建立的開源 Adobe AfterEffects 動畫網路播放器,被公認為難以置信的贊。不幸的是,它也很龐大。這裡的龐大指的是動畫執行時間和動畫資料本身,後者是一大堆 JSON。我們載入了 JSON 檔案作為動畫 JS 的一部分而不是使用 JSON.parse,如此我們便可以在主執行緒之外解析這些資料。

指令碼載入

JSConf EU 網站實際上載入了一些 JavaScript —— 但它不會阻止頁面繪製,我們內聯了互動所需要的每一塊程式碼。這樣當瀏覽器繪製頁面的時候,不管外部 JS 是否已經載入完畢,所有關鍵的互動都已經在起作用了。雖然這個不會讓內容安全策略(CSP)滿意,但是不要慫,及時行樂,哈哈哈。我們載入的 JS 檔案也不會改變 DOM,即不會觸發額外的繪製(當然,除了動畫以外),所以無論瀏覽器首先繪製了什麼內容,在發生使用者互動之前它都不會發生改變。

預載入

除了樂一樂,我們希望大家在這個網站上做的主要事情之一就是買票。這就是為什麼我們在頁面載入的早期就預先載入了票務支付提供商的登入頁面的原因,以便當你決定買票(哈哈哈,我覺得應該買!)的時候,它就可以立刻載入。另外,我們預載入了從主導航連結到的所有頁面,因此在網站內的導航速度非常快。不幸的是,並不是所有的瀏覽器中都有預載入的功能,但是社群最近真的用了很多時間使它可以和 Safari 的雙鍵快取基礎設施相容,所以我們有望在所有瀏覽器中使用它 —— 儘管不是在 JSConf EU 2019 中。

HTTP 級聯

這個網站繪製使用了一次 HTTP 往返,並且從不需要獲取一個 HTTP 資源用來確認除了主文件之外還需要獲取其他內容。這意味著 HTTP 級聯的最大深度為 2 次請求。特別是在移動裝置上,頁面載入時間通常由延遲決定。在這些情況下,具有一個扁平的請求級聯意味著網路延遲會帶來較少的負面影響,或者讓我們返回到時間複雜度 O 上:在延遲和可用或所需頻寬關係很大的情況下,頁面載入時間是 O(延遲 * HTTP 級聯深度 + 下載內容用時)。能夠始終使用 1 次 HTTP 往返繪製意味著在許多情況下頁面的載入時間由 DNS 和 TLS 連線決定。這些在實驗測試中經常看起來很糟糕。但有了 CDN 對 TLS1.3 甚至 Quic/HTTP3 的支援,真實情況下的效能看起來會好很多,特別是對於那些重複訪客。

2019.jsconf.eu 網址的請求資料流
一個非常扁平的 HTTP 級聯(右下角是 ServiceWorker 在啟動,它不會影響原始頁面載入)。

影象載入

OMG,我討厭載入影象時沒有預先知道它們的大小,並且當它們通過網路傳輸時,它們會推遲頁面的顯示。在我們的網站上,所有的影象要麼有一個靜態的大小,要麼使用 padding-top: XX% hack 使影象擴充套件到所有可用的水平空間。其他人還使用了 intrinsicsize 屬性(目前沒有瀏覽器支援)(如此便可以不使用剛才的 hack) 和 懶載入屬性(目前也沒有瀏覽器支援)以及decoding=async(目前大部分瀏覽器支援)來避免影象解碼阻塞主程式。

什麼是 CSS 中的寬高比?他們說是 padding-top 百分比。Jan 18, 2018

(小小的諷刺警告:上面推文的圖片拖慢了頁面,但是我把它放在了頁面的這個位置,你可能都不會注意到(如果你沒有看到這裡的話),實在是不好意思)。

總結

就是這樣啦。在 2019 年製作快速網站並不是一件很難的事情。你只需要這瀏覽器製造商打好關係,付他們錢就讓網站速度更快,在 Twitter 逛一整天你都可以獲得關於效能的熱門廣告,或者你更願意自己動手對效能做些小改動而不是看 Netflix。

致謝

謝謝 Malte Ubl 撰寫這篇文章。這個網站本身最初是由過於有才華的 Lukasz Klis 開發,而令人稱讚的設計則是由超酷的 Silke Voigts 為我們帶來,謝謝他們。

如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區kuai鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章