EP11 - 升級Vue 3的經驗及過程

之前Vue的創作者Evan去了台灣參加conference講了當時開發Vue 3的過程學到了什麼

也讓我回想到我當初從Vue 2到3的過程

這邊分享一下 或許中間的經歷對大家會有點幫助

也有可能因為Vue 3也出來很久了我當時的狀況現在沒有了

講一下當時背景 我們當時剛從Java裡面的前端搬出來變成一個自己的前端SSR instance

這樣我們前端有自己的repo 自己的pipeline 而且因為PR merge時候也不需要build Java後端的東西所以整體做事效率有顯著的提升

這麼做對整體的application performance有很明顯的提升 不過我們想再試試有沒有更進一步的空間

我們把我們的注意力轉到了Vue 3

2019年去了多倫多參加Vue Conf而Evan親自講說Vue 3帶來了很多改動讓整體使用者開發體驗和使用者體驗變好

不過當時Vue 3才剛發布不久而且沒有很多project有在使用Vue 3

雖然官方的文件算是滿好的但社群的資料還是偏少

再者 當時Vite剛出而Vue 3的資料蠻多是跟Vite一起做的

這對當時我們基於Vue 2和Webpack的架構來說增加了不少的複雜性和一定的不可預測性畢竟Vite蠻新的也沒有很多人熟悉rollup所以有時候不太清楚為何會有bug

下面講一下我們當時遇到的問題及思考邏輯

Vite

剛才提到的Vite,當時我們是基於Webpack 4做的整個開發者架構而Webpack最為人詬病的就是它的configuration和越來越慢的bundle速度尤其是dev server

而Vite帶來的就是非常簡單的設定和它快速的dev server

Vite不像webpack需要全部都build好,有一點像是lazy loading的思考方式從HTML出發看需要先transpile什麼bundle來給你畫面

我們POC測試時候整個感覺非常好也幾乎可以說是即時的可以起local server和反應新的change

不過當時Vite這個讓我們有點卡住

因為Vue 3大部分的東西都是和Vite走的所以我若用Vue 3我基本上Webpack的資料就很少

加上我們當時是用SSR Vue 3的資料更少了網上幾乎沒有人做這個

唯一的documentation是Vue官方自己寫的當時大概就是三段話吧而且好像還是寫在Vite那邊的有點不記得了

沒有前車之鑑等於我們自己就是前車

後來是一遍又一遍的測試才把Vite Vue SSR弄出來可以跑起來

這中間也遇到不少bug而且有時候還不確定是Vite還是Vue的

Vue 3

Vue 3帶來了很多改變以及效能上改善

比如VirtualDOM的演算法優化,Vue compiler可以知道當state更新之後哪一個DOM需要被更新而不是整個component rerender

新的Composition API語法也讓這個component的code變的比較有條理也好整理

加上整體感覺比較像是在寫純JavaScript的function所以寫起來比較順手

VSCode intellisense也比較看得懂

當時還沒用TypeScript但Vue 3因為也是TypeScript寫的整個developer experience應該是會有感覺的

其他一些語法的改變比如V-Model和 vfor vif的先後順序

不支持舊版本的瀏覽器

前面其實都好說還算比較簡單的改動

不用支援IE對工程師來說是好消息,不用為了IE而做一些神奇的workaround

而且不支援IE我們的JS bundle也會自動變小,不用多做很多的transpilation和polyfills

不過我們花了一些時間和marketing溝通這件事情

對他們來說能觸及到越多人肯定是越好的但帶來的是工程師的overhead

我們找了很多資料來溝通說工程上的進步能幫到公司做到更多的事情和快速驗證很多實驗

加上我們在Google Analytics上面看到過去三個月IE的流量不到1%而在這1%有真的convert的人才3%

基本上從用IE的人賺到錢計畫少之又少 最後marketing被成功說服我們繼續Vue 3的migration

這時就遇到一些不清楚是Vite還是Vue的bug

比如我印象深刻的是Vue 3有個breaking change是我本來可以單純用dynamic import來lazy load一個component

但Vue 3需要用一個新的function來做

結果在SSR情況下如果這個async component之前有個component只在client render比如用v-if來toggle,SSR會報錯而且這個error大概只有Vue contributor自己看得懂了

後來在Vue那邊開了issue也被修好了才得以繼續下去

Vue 3也帶來了Composition API 不過Vue team之前有為了Vue 2而publish一個可以讓Vue 2也是用Composition API的package所以在Vue 2期間我們就已經用上了

然後當時我們看Composition API太好用了

感覺就在寫JavaScript function然後不像React那樣useState只能在component裡面

ref variable可以在任何地方設定

我們直接把這些composition function寫在function外面

任何import這個function的component都可以用到這些states

這樣就弄出一個簡單的state management 之後也索性把Vuex給拿掉了

所以在Vue 2我們就是這樣用Composition API來做我們的state management

不過這後面要弄SSR時候冒出了一個問題等等會提到

SSR

當時我們整個架設起來 東西可以跑的時候真的都快哭出來了

但遇到了兩個滿大的bug

第一個我把他歸類到memory leak

Vue做SSR時候應該會重複利用已經編譯過的template

但當時看到的是 如果有十個request到同一個頁面

memory裡面會有10個一模一樣的template而且memory reference還不一樣

就算手動在node server叫garbage collector也清不掉 等於他們被認定是說在某個地方有被referenced

不曉得從哪裡開始debug的我開始刪code

把那個頁面的Vue template開始刪

最後是template裡面其實有個error被吃掉了所以工程師不知道但Vue內部處理過程就做出剛才講的事情

那個error有點類似如果那個ref variable是undefined的話v-bind會看不懂

它有個特定的寫法所以我們把所有這些寫法改掉之後就沒有這個bug了

第二個是state被共用了

剛才前面提到我們用Composition function來做state management而那些state是在function的外面而不是裡面

所以其他component在import這個function時候是會拿到被改過的state而不是全新的value

結果在SSR時候因為一直都是同一個instance所以這個state是會污染到其他request的

這在Vue 2 SSR時候是沒有發生的

後來發現Node有個module叫做VM

讓你可以在不同的V8 context下跑你的code

也就是說每一個traffic進來都會是獨立的在跑就不會影響到別人

有趣的是我後來去找之前Vue 2的setup發現這個本來是有在Vue 2裡的

不曉得為何Vue 3拔掉了

我們中間做load test時候在沒有caching的情況下Node server表現的還是很好所以沒什麼大問題

當然如果當時用Vuex什麼的可能就沒有這個問題了

但當時我們太喜歡Composition function了加上當時這些function去拿Vuex store裡面的資料挺噁心的所以還是繼續這樣使用composition function了

最後總結是

我們developer對Vue 3和Vite的變動感到很開心 整體速度非常快而且有比較像是在寫JavaScript

但在整體的效能提升上有一點幫助沒有特別明顯

lighthouse的分數上整體上升5分

這邊給個bonus當時我們做了什麼讓分數從40分到將近90分

是因為我們把google tag manager延後了4.5秒

當時大部分的90%以上的session是會停留超過4秒的所以我們想說能不能做點compromise把GTM延後loading

也因為這樣lighthouse分數沒有把GTM納入進去就會覺得我們加載剛剛好的JS

如果你喜歡這內容的話幫我在 Twitter 和 Threads 上面分享給其他正在前端這條路上努力的朋友們,也別忘了訂閱我們電子報收到第一手消息喔 🚀