在去年底,營運單位真的受不了我們家Mobile Web的超級慢的反應速度啦! 因此,我們在公司組成了約10人的專案小組,決定重新開發這個產品,經過了三個月左右的開發,這款Remasterd版本就會在不久的未來上線了。 所以在這裡分享一下我們重製的心得。
影響原作慢速的關鍵點
其實在一開始,其實我們已經知道了一些明顯效率不足的問題,那麼接下來再透過一些工具來設想,只要排除這些問題,在讀取或使用者回饋上就可以獲得大幅度的優化。
主要問題與初步解決方式:
- 利用檔案格式與ImageLoader解決...過大的圖片與過度渲染的問題
- JSBundle Size太大,我們將採用最極端方式解決
- 必要的API載入要花10秒以上,沒關係我們跳過它們
利用檔案格式與ImageLoader解決...過大的圖片與過度渲染的問題

打開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)過程中,就將圖片的事搞定。

JSBundle Size太大,我們將採用最極端方式解決
webpack的調校,一直是前端最重要的事情。
如果對於太龐大的專案,例如需要安裝不少Library、龐大的多語系檔、不少的redux store加上redux-saga,想瘦真的很有難度。
基本的切割術
一般專案在針對js bundle size優化,主要會以下方條例為主(歡迎補充少提到的部份),當然這個是基本中的基本:
- 使用lazy load載入component
- redux可以做到分批載入,利用reducer compoose,就可以不用一次載入全部reducer
- 切記圖檔、SVG等等通通放進public裡,不要在Component中import
- 要考慮即將要用的library是不是有過大的問題,或3rd library是否有提供單項載入function的API;例如lodash可以按需求安裝
圖:可以用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界誕生了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來說,就顯得沒那麼嚴謹。

有時,Redux使用上有時設計的太過嚴謹,或大量的使用Redux儲存資料,就會有Over design的問題,這一些看開發者怎麼想了。
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檔案大小與強化所有的共用空間
所以,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有興趣,可以三個都試一下,選出最適合貴團隊的。

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

結論
很快就到了結論。
在我的認知裡,專案優化就是由經驗而來,而且將不會一步到位,也不用做到全滿造成過度設計,畢竟還是有時程上的壓力。技術選型並沒有絕對值,而是看當下的需求再來決定,這一次我們的需求就是讓整體速度越小越好,並且去除所有不必要的Client運算,將Loading給予了動畫效果。因此,就做出了本文章所說的重構方案。
當然還有很多細節沒講,例如例用動畫讓UX感覺更流暢、必要的code review flow等等,也都應該要在前端優化的項目之中一併討論。
或許過了一年,又有要使用者感覺更快的需求,那時,可能本篇文章講的技術又被放棄了XD