- 原文地址:A Tinder Progressive Web App Performance Case Study
- 原文做者:Addy Osmani
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:pot-code
- 校對者:zwwill
最近 Tinder 在 web 端發力,公佈了 PWA 應用 Tinder Online ,現已全平臺支持。在技術實現上,爲了應對 JavaScript 性能優化問題 採用了最前沿的技術,例如,使用 Service Workers 來應對網絡彈性問題、使用 消息推送(Push Notifications) 來支撐聊天業務。本文將向各位講解大佬們是如何處理開發中的性能優化問題的。javascript
Tinder Online 肩負着打開新市場的使命,它背後的團隊但願把它打形成一個全平臺無縫體驗的在線聊天平臺。php
產品的 MVP 開發花了 3 個月,UI 庫用了 React,狀態管理用的是 Redux。最後的成果仍是顯著的,在不影響功能體驗的前提下,數據傳輸量減小到了原來的十分之一:css
上圖是 Tinder Online 和手機 app 在安裝過程當中所需數據量大小的比較,雖然這兩個類型不一樣,可是仍是具備參考意義的。 相對手機 app 來講,PWA 只有在須要時才加載新的代碼。用戶邊使用邊下載,由於下載過程分散在整個使用過程當中,因此用戶並不會察覺到。即便用戶在使用中訪問了應用的其餘部分,綜合下載量也仍是少於直接下載整個 app 所需的數據量。html
上線後的前期表現仍是不錯的,用戶劃一劃頻率、發信頻率以及在線時長均優於在手機 app 上的表現。總之,使用 PWA 以後(針對 PWA 和原生的比較):前端
數據顯示,手機端用戶主要使用的設備包括但不限於:java
再使用 Chrome User Experience report (簡稱 CrUX)進行分析,得知用戶主要使用 4G 網絡進行訪問:react
注:能夠參考 Rick Viscomi 在 PerfPlanet 上對 CrUX 的介紹,Inian Parameshwaran 介紹的 rUXt 在網絡可視化分析這塊針對大流量網站更具優點。android
在經常使用的 web 應用測試網站(WebPageTest 和 Lighthouse)上進行測試,使用 4G 網絡的 Galaxy S7 能夠在 5秒內 徹底加載應用:webpack
相比高端機型,配置 相對通常 的機型(例如 Moto G4)的性能表現還有很大的提高空間,這類機型的 CPU 資源比較吃緊:ios
Tinder 如今也確實在針對這方面作優化,期待他們之後的表現。
Tinder 爲了加快頁面的加載使用了不少技術,例如基於路由的代碼分割、性能預算(performance budgets)以及靜態資源持久緩存。
起初打包的文件很是巨大,嚴重拖慢了交互就緒的速度,對用戶體驗影響很大。由於打包的文件裏包含了除核心交互之外的代碼,這就須要使用 代碼分割(code-splitting) 抽離出暫時不須要的代碼,只保留核心功能。
針對這個問題,Tinder 引入了 React Router 和 React Loadable。得益於前期架構的優點,整個應用很是適合使用基於配置的方式處理路由和渲染,所以,代碼分割也能很順利的在頂層上實現。
小結:
React Loadable 是 James Kyle 開發的一個輕型庫,簡化了 React 中基於組件的代碼分割操做,它提供的 Loadable 函數是一個高階(higher-order)組件(建立組件的函數),專門用於處理代碼分割。
下面舉例說明。假若有兩個組件 「A」 和 「B」,分割前,它們和入口文件一併打包,由於這兩個模塊目前並不須要,因此這樣所有打包在一塊兒是很低效的:
這裏要求使用代碼分割以後,組件 A 和 B 只是在須要時才加載。爲此,Tinder 引入了 React Loadable、動態導入函數 import() 以及 webpack 神奇的註釋語法 (用來爲動態導入的模塊命名):
在公共庫(即 vendor)的處理上,Tinder 使用了 webpack 官方提供的 CommonsChunkPlugin 插件,把路由間公用的庫單獨打包到一個文件中,利用瀏覽器的緩存機制提高性能:
接着使用 React Loadable 提供的預加載功能 針對那些/在接下來的交互中/存在潛在加載需求/的資源/作預加載處理(譯者注:注意斷句):
Tinder 還用 Service Workers 對全部路由作了預緩存處理,其中就包括用戶常常訪問而沒有作代碼分割的路由。最後使用 UglifyJS 對代碼進行壓縮:
new webpack.optimize.UglifyJsPlugin({
parallel: true,
compress: {
warnings: false,
screw_ie8: true
},
sourceMap: SHOULD_SOURCEMAP
}),
複製代碼
使用代碼分割後,打包文件大小從 166KB 減到 101KB,DOM 內容加載時間(DCL)也從 5.46s 下降到 4.69s:
在 webpack 的輸出中配置 [chunkhash],一方面能夠用來規避開發過程當中因瀏覽器緩存而引起的資源不更新問題,二來也能夠確保緩存有效。
因爲 Tinder 使用了大量的第三方開源庫,若是其中的一個依賴發生變化都會致使 [chunkhash] 的從新計算,從而致使以前的緩存失效。爲了解決這個問題,Tinder 制定了一個 外部依賴白名單,另外還將 manifest 從主幹中抽出,進一步改進緩存。最後,主幹代碼和 manifest 的打包大小都只有 160KB。
這裏用到了 <link rel=preload>
,它告訴瀏覽器這是一個關鍵資源,立刻要用到,須要儘早加載。在單頁應用(SPA)中,這些資源能夠是打包後的 JavaScript 文件。
Tinder 將核心體驗相關的資源文件進行了預加載處理,最終加載時間減小了 1s,首次繪製時間也從 1000ms 減小到 500ms。
爲了達到移動端的性能指望,Tinder 引入了 性能預算。Alex Russell 在他發表過的一篇文章(Can you afford it?: real-world performance budgets)中指出:難就難在要在網絡環境很差、配置也通常的移動設備上提供良好的用戶體驗,由於提高空間很是有限。
爲了保證應用能快速就緒,Tinder 規定公共依賴和主幹代碼的大小要維持在 155KB 左右,分配給異步加載(懶加載)的數據量大約 55KB 左右、其餘部分代碼 35KB 左右,CSS 則限制在 20KB 左右。 這個規劃保證了最壞狀況下的性能表現。
開源工具 Webpack Bundle Analyzer 能夠對依賴進行可視化分析,暴露出那些明顯的可優化問題。
Tinder 主要用它來分析如下幾類優化問題:
這個工具能夠搭配 Webpack 的 Lodash Module Replacement 插件 使用。這個插件將模塊中使用到的一些特定方法替換成實現上更簡單、功能上等效的代碼,從而減少打包文件大小:
Webpack Bundle Analyzer 能夠集成到 Webpack 的配置中。來自 Tinder 的配置參考:
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'server',
analyzerPort: 8888,
reportFilename: 'report.html',
openAnalyzer: true,
generateStatsFile: false,
statsFilename: 'stats.json',
statsOptions: null
})
複製代碼
通過一系列優化以後,剩下的主要是主幹部分代碼。這些代碼暫時就不用動了,除非架構發生變化。
Tinder 使用 Atomic CSS 建立了許多高複用的 CSS 樣式,其中一些作了內聯處理,在初始化繪製過程當中生效,剩下的則分散在外部 CSS 文件中(包括動畫或者 base/reset 等樣式)。關鍵樣式(Critical styles)使用 gzip 壓縮以後大小不超過 20KB,最近的一次構建則壓縮到了 11KB 如下。
Tinder 還使用了 CSS stats 和 Google Analytics 跟蹤每次發版的變化。在使用 Atomic CSS 前,頁面的平均加載時間在 6.75s 左右,使用以後降到 5.75s 左右。
最後用 Autoprefixer 添加瀏覽器前綴:
new webpack.LoaderOptionsPlugin({
options: {
context: paths.basePath,
output: { path: './' },
minimize: true,
postcss: [
autoprefixer({
browsers: [
'last 2 versions',
'not ie < 11',
'Safari >= 8'
]
})
]
}
}),
複製代碼
使用 requestIdleCallback() 將非關鍵事務推遲到空閒期運行,以提升運行時性能。
requestIdleCallback(myNonEssentialWork);
複製代碼
什麼叫非關鍵事務?好比作插樁標記(instrumentation beacons)。Tinder 團隊爲了減小刷屏時的繪製次數,對合成層(composite layers)也作了優化。
在刷屏操做中,是否使用 requestIdleCallback() 的對比:
使用前..
使用後...
Webpack 3 + 做用域提高
在 webpack 3 之前,每一個模塊在打包後都會被包裹進一個獨立的閉包內,可是這些包裹起來的方法(閉包)執行效率很低。對此,Webpack 3 推出了「做用域提高」機制,將多個模塊打包進一個閉包中(至關於合併做用域),以提升執行速度。使用這一功能須要引入 Module Concatenation 插件:
new webpack.optimize.ModuleConcatenationPlugin()
複製代碼
使用做用域提高機制後,Tinder 第三方依賴初始化解析時間減小了 8%。
React 16
React 16 優化了打包以後的文件大小,新的打包算法(Rollup)會剔除一些暫時用不到的代碼。
Tinder 從 React 15 升級到 React 16 後,gzip 壓縮後的依賴大小減少了約 7%。
最後,react + react-dom 壓縮後從以前的 50KB 降到如今的 35KB 左右,效果拔羣。這裏要特別感謝 Dan Abramov、Dominic Gannaway 和 Nate Hunzaker 的付出,他們在 React 16 中爲下降打包文件大小作了很多貢獻。
Tinder 還用了 Workbox Webpack 插件,用於緩存 Application Shell 和核心靜態資源,例如主幹代碼、第三方庫、manifest 和 CSS。使用後,具有了較強的頻繁訪問抗壓能力,同時用戶再次使用時,啓動速度也大大提升了。
用 source-map-explorer (又一個包分析工具)再次對包進行深刻分析,發現還有繼續縮小網絡負載的優化空間。在登錄場景中,用戶還沒登陸的狀況下,Facebook 圖片、通知、私信以及驗證碼這些組件並不須要加載,將這些組件從關鍵路徑(critical path)移除以後可爲主幹代碼省下 20% 的數據量:
由於上一步移除了 Facebook 的組件,因此其相關依賴 Facebook SDK 也能夠直接移除,後面須要用到的時候再進行加載(懶加載),這樣就又節省了 200KB,並且初始加載時間也減小了 1 秒。
雖然 Tinder 還在繼續迭代他們的產品,可是已經初見成效了,你能夠隨時訪問 Tinder.com 關注最新進展。
感謝並祝賀 Roderick Hsiao、 Jordan Banafsheha 以及 Erik Hellenbrand,恭喜大家成功發佈了 Tinder Online,謝謝大家對個人文章提出的指導意見,還有 Cheney Tsai,你的觀點給了我不少啓發。
相關閱讀:
本文轉載自 Performance Planet。 若是你對 React 還不熟悉,能夠參考我推薦的教程:React for Beginners,對新手十分友好。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。