ARCTICLE

Mobile Web 從 10000 變 100的速度

在去年底,營運單位真的受不了我們家Mobile Web的超級慢的反應速度啦! 因此,我們在公司組成了約10人的專案小組,決定重新開發這個產品,經過了三個月左右的開發,這款Remasterd版本就會在不久的未來上線了。 所以在這裡分享一下我們重製的心得。

影響原作慢速的關鍵點

其實在一開始,其實我們已經知道了一些明顯效率不足的問題,那麼接下來再透過一些工具來設想,只要排除這些問題,在讀取或使用者回饋上就可以獲得大幅度的優化。

主要問題與初步解決方式:

  • 利用檔案格式與ImageLoader解決...過大的圖片與過度渲染的問題
  • JSBundle Size太大,我們將採用最極端方式解決
  • 必要的API載入要花10秒以上,沒關係我們跳過它們

利用檔案格式與ImageLoader解決...過大的圖片與過度渲染的問題

Network圖

打開Chrome的devtool,看到舊專案中,無論在JS的chunk大小、後端API response時間到圖片檔案大小都有滿大問題,有的圖檔居然要2MB以上;而前端自己做的圖則都使用png格式。
所以我決定將圖檔格式轉為壓縮率可提高50%以上的的webp格式,就可以解決大型圖檔的問題。

但可惜,不是所有瀏覽器都支援webp。 所以說圖檔的格式重構,我分為前端與後端兩部份;

前端的圖檔通常是由設計稿出來,其實單純很多,如果貴團隊是透過figma來出設計稿,那就可以加裝webp外掛。
在製作過程中,把設計稿上的圖輸出為webp與png,然後使用於前端。

但上述方法比較沒那麼有彈性,所以我們可以加入一點後端服務協助轉格式與進行圖檔壓縮。 如果貴團隊有預算,可以直接採用imgix這種圖片即時轉檔服務; 不然,也可以自己做類似於imgix的小型服務。 透過這種API圖片轉檔服務,可隨時修改quality或圖片格式,為前端工程在設計稿的輸出部份,創造出了更多彈性,也節省了很多開發上的時間。

那圖片轉檔服務提供了什麼彈性呢? 例如畫面上某一塊圖片,僅僅需要300px寬度的圖,但原設計稿出來卻是2000px,以前的我們,就會拿去找軟體轉換其大小,但有了圖片轉檔服務,就只要使用下方的圖片路徑,就可以輕鬆轉為我們要的東西。 ‵‵‵ https://圖片轉檔服務/home/logo.png?format=webp&width=300&quality=80 ‵‵‵ 使用了上方的URL,顯示出來的圖片,就會是一個webp檔案,且寬度為300的圖片,圖片壓縮率達90%以上(老闆就更開心了)。

以下是對於圖片轉檔服務的粗略流程圖,簡單來說,會在building (CI)過程中,就將圖片的事搞定。 簡單的ImageService流程圖

JSBundle Size太大,我們將採用最極端方式解決

webpack的調校,一直是前端最重要的事情。

如果對於太龐大的專案,例如需要安裝不少Library、龐大的多語系檔、不少的redux store加上redux-saga,想瘦真的很有難度。

基本的切割術

一般專案在針對js bundle size優化,主要會以下方條例為主(歡迎補充少提到的部份),當然這個是基本中的基本:

可以用webpack-bundle-analyzer評估CodeSplit還有哪些地方待優化 圖:可以用webpack-bundle-analyzer評估CodeSplit還有哪些地方待優化

關於優化,建議採建漸式優化,你們可以掛載ga來觀察各頁面使用狀況;舉例來說,有某一頁他背後有10組Dialog,結果透過分析發現使用者根本不會開,這10個Dialog根本不在必要的render項目中,那麼就可以把Dialog轉用lazy load方式載入。

標題已經有提到,我們決定採用最極端的方式解決,例如廢除redux也在選項之中。

採用recoil、swr做為store管理方案

Redux或MobX對於React都是非常重要的資料管理框架,就Redux來說,將資料與邏輯拆分,搭配unit test撰寫,在資料的流動性或準確度都可以精準的控制住。

但對於Mobile Web這種有許多大量單頁資料來說,所有頁面的資料都放在Redux,並使用ReduxSaga去控管,會不會又太龐大了一些?

React搭配Redux生態系

還好去年在React界誕生了SWR這個以API為主的資料管理系統;SWR將透過開發者給予的URL做為KEY值,很方便的將後端API回傳的資料存在KEY裡,還提供了cache控管、ERROR處理等開發者必定會用到的狀況處理。

在改採用了SWR後,解決了非常多局部資料的問題,但我認為這僅只於適用儲存單一頁面或單一元件上的資料管理。

而user等Global state等經常性的跨元件資料,就不太適合使用SWR。

要徹底移除Redux,全域資料佔比不大的狀況下,其實使用React Context就可以處理大部份資料儲存問題;但有時需要解決Context的坑或LocalStorage的同步問題,在發懶的狀況下,就採用了React Recoil的解決方案。

Recoil是相當輕便型的資料管理套件,國外已慢慢在採用,只需要設定Atom(類似ReduxReducer)或Selector,就可以在Component裡,用hookAPI讀寫特定key的資料。

Recoil的操作其實與ContextAPI非常類似,如果很常使用ContextAPI,絕對可以在短時間內將Context移到Recoil;而Recoil有內建如Selector、Effect(Unstable)的使用方式,用起來爽度加倍,但相較於Redux來說,就顯得沒那麼嚴謹。

React搭配Recoil存GlobalState, SWR存state by API/GQL

有時,Redux使用上有時設計的太過嚴謹,或大量的使用Redux儲存資料,就會有Over design的問題,這一些看開發者怎麼想了。

Recoil使用方式類似ContextAPI,但也提供更多功能 Recoil使用方式類似ContextAPI,但也提供更多功能

關於SWR的使用方式就不說太多了,以後有時間再來寫,不如上SWR官方網站看並試做一下,就可以在一小時理解SWR。 而且...對於Redux整合API的數據問題,我個人更愛全團隊採用GraphQL + SWR解決長久以來的資料整合問題

不再使用CSSinJS,採用tailwind css

CSSinJS在這幾年也很火紅,使用styled-components或著是emotion,再搭配styled-system等輔助,可以很快的設計出UI變動性高的Component。

但styled-components + styled-system,我再寫一些ThemeProvider,然後各Component的styled.js又再加一些東西....嗯,是不是變的肥大了起來。

在能省1kb就省1kb的狀況下,決定採用tailwind處理所有UI問題。

前端就是很有趣,這環境是一個輪迴,在之前大家都愛用SASS,這幾年大家更愛用CSSinJS,但沒想到又橫空殺出了tailwindCSS,看起來又回到了CSS時代。

但,tailwindCSS無疑是目前最強大的解決方案,它是一套會自動歸納的css library,可以將專案下的所有style做整合,變成一個小小的main.css去控制90%的Component Style。

想想,本來一個上百頁的專案中,其styles可能佔了200~300kb;但使用了tailwindCSS之後,可能只變成了20kb左右;而且css檔案獨立於js之外,不一定要等js載入後才能看到完整的畫面。

Tailwind的確可以省下90%的css檔案大小 Tailwind的確可以省下90%的css檔案大小與強化所有的共用空間

所以,tailwindCSS對我來說....

  • 省略了ThemeProvider、StyledComponent檔案大小
  • 將專案下的所有style class進行了歸納
  • 將強制規範成員們撰寫style的方式

最後,還有一個更強大的優點...

我將tailwindcss做為了設計師與工程師的唯一規範管道,設計師的DesignSystem全部歸納進tailwind設定檔裡面,接下來的專案製作,減少了許多不必要的誤差與討論時間。
延伸文章:前端與設計與tailwind

必要的API載入要花10秒以上,沒關係我們跳過它們

在舊的系統中,我們的一些API或JSON檔,是必要的資訊,沒有這些資料無法順利跑出畫面,但偏偏要一個等一個,經常要耗時9秒。

專案要看到第一個畫面的必要API,提供了...

  • 商家全站的版型與Color Set (API response: 3s)
  • 首頁的必要遊戲資料 (API response: 6s)

所以,我決定採用static HTML的方案,來解決這個問題...

採用Next輸出Static HTML

關於static html的使用,主流三大選項是...

  • Nextjs
  • Gastby
  • React Static 如果您對於static html有興趣,可以三個都試一下,選出最適合貴團隊的。

React主流三套StaticHtml解決方案

Vue的StaticHTML解決方案,也可以參考 NUXT JS

所以經過(Y)&_*()^%的思考後,我決定採用Nextjs來重作整個系統。

透過NextJS輸出成static HTML的好處很多,第一項就是絕對可以大幅度縮減FP的時間; 但這對我的好處不只是如此,而是可以不用再Client import資料型態的JS了,我讓這些靜態資料型態的js或API,在Building過程中,即轉為HTML,一來一往其實已經省下了2kb~10kb的時間。

而且以前要等js載入完,onLoad之後再跑js的程式,再顯示畫面;全面優化為直接html載入完即可看到資料,有感程度搞不好在1秒以上。

現在是5G時代,但只要您的東西比別人快,網站PR就是會往前排。

static HTML 對我們重構案造成的優點

  • 省略靜態資料的js或api載入時間
  • 完全刪掉了靜態資料在Client還需要做資料整理的Loading,我們將此Loading拿去做特效了
  • 輕鬆完成了SEO

以這個站台來說,其實article資料都寫在firebase,但在編譯時就轉成了HTML,所以在Browser端看不到有任何call api的資訊,就可以大幅度提高整體速度。

結論

很快就到了結論。

在我的認知裡,專案優化就是由經驗而來,而且將不會一步到位,也不用做到全滿造成過度設計,畢竟還是有時程上的壓力。技術選型並沒有絕對值,而是看當下的需求再來決定,這一次我們的需求就是讓整體速度越小越好,並且去除所有不必要的Client運算,將Loading給予了動畫效果。因此,就做出了本文章所說的重構方案。

當然還有很多細節沒講,例如例用動畫讓UX感覺更流暢、必要的code review flow等等,也都應該要在前端優化的項目之中一併討論。

或許過了一年,又有要使用者感覺更快的需求,那時,可能本篇文章講的技術又被放棄了XD

CONTACT