之前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,當時我們是基於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帶來了很多改變以及效能上改善
比如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時候冒出了一個問題等等會提到
當時我們整個架設起來 東西可以跑的時候真的都快哭出來了
但遇到了兩個滿大的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