Vue2 徹底從 Flow 重構(gòu)為 TypeScript,煥然一新!
事情起源于 4 月 7 號(hào)晚上,尤雨溪在推特說(shuō),Vue2 收到了一個(gè)將整個(gè)代碼庫(kù)遷移到 TypeScript 的 PR。

去 Github 圍觀了一下chore: move to typescript 這個(gè) PR[1],基本上是 10w 行級(jí)別代碼量的改動(dòng),把整個(gè) Vue2 的代碼庫(kù)從原先的 flow 類(lèi)型系統(tǒng)全部遷移到了 TypeScript,包括代碼、構(gòu)建系統(tǒng)、各種 lint 工具等等,恐怖的戰(zhàn)斗力!

這個(gè) PR 的貢獻(xiàn)者是 Carlos Rodrigues[2],以下是他的自我介紹:
Fullstack developer, interested in @vuejs, @dotnet and @nodejs.
Typescript ???♂?
Consultant ????♂?
全棧開(kāi)發(fā)工程師,Vue.js、dotnet、Node.js 的愛(ài)好者。
TypeScript 魔法師 ???♂?
顧問(wèn) ????♂?。
起源
Vue2 為什么最開(kāi)始選擇 Flow 作為類(lèi)型系統(tǒng)?其實(shí)在一個(gè) 2016 年的知乎問(wèn)題Vue 2.0 為什么選用 Flow 進(jìn)行靜態(tài)代碼檢查而不是直接使用 TypeScript?[3]里,尤雨溪已經(jīng)詳細(xì)說(shuō)明了這個(gè)問(wèn)題,以下是當(dāng)時(shí)他的回答:
這個(gè)選擇最根本的還是在于工程上成本和收益的考量。
Vue 2.0 本身在初期的快速迭代階段是用 ES2015 寫(xiě)的,整個(gè)構(gòu)建工具鏈也沿用了 Vue 1.x 的基于 ES 生態(tài)的一套(Babel, ESLint, Webpack, Rollup...),全部換 TS 成本過(guò)高,短期內(nèi)并不現(xiàn)實(shí)。
相比之下 Flow 對(duì)于已有的 ES2015 代碼的遷入/遷出成本都非常低:
可以一個(gè)文件一個(gè)文件地遷移,不需要一竿子全弄了。 Babel 和 ESLint 都有對(duì)應(yīng)的 Flow 插件以支持語(yǔ)法,可以完全沿用現(xiàn)有的構(gòu)建配置; 更貼近 ES 規(guī)范。除了 Flow 的類(lèi)型聲明之外,其他都是標(biāo)準(zhǔn)的 ES。萬(wàn)一哪天不想用 Flow 了,用 babel-plugin-transform-flow-strip-types 轉(zhuǎn)一下,就得到符合規(guī)范的 ES。 在需要的地方保留 ES 的靈活性,并且對(duì)于生成的代碼尺寸有更好的控制力 (rollup / 自定義 babel 插件)
不過(guò)在 2018 年的時(shí)候,尤大更新了回答,真香定律再現(xiàn):

也正因如此,Vue3 從一開(kāi)始就選擇了 TypeScript 作為類(lèi)型系統(tǒng)。
困擾
那么也許有人要問(wèn),Vue2 不是已經(jīng)穩(wěn)定了嗎,何必再大費(fèi)周章的把這么多代碼遷移到 TypeScript 中呢?其實(shí)在之前 Vue3 放棄 IE11 的 RFC 中就有提及,之后還是會(huì)為 Vue 2.7 去加入一些和 Vue3 語(yǔ)法更類(lèi)似的功能:
把 @vue/composition-api plugin[4]合并進(jìn) Vue2。這會(huì)讓使用 Composition API 開(kāi)發(fā)的庫(kù)同時(shí)支持 Vue2 和 Vue3。 單文件組件(SFC)中的script setup[5]語(yǔ)法。 emits選項(xiàng)。提升 TypeScript 類(lèi)型支持。 在 Vite 中正式支持 Vue 2(目前通過(guò)非官方插件[6])
而這些功能的開(kāi)發(fā)和適配,如果繼續(xù)用 flow 的話,勢(shì)必會(huì)帶來(lái)一些割裂的開(kāi)發(fā)體驗(yàn)。一些已經(jīng)用 TS 開(kāi)發(fā)好的庫(kù),也沒(méi)辦法做代碼的合并。事實(shí)上 Twitter 也有網(wǎng)友提出了這個(gè)問(wèn)題,PR 作者進(jìn)行了回答:

簡(jiǎn)單來(lái)說(shuō),就是為 Vue 2.7 的開(kāi)發(fā)做準(zhǔn)備,尤其是 composition-api 的代碼合并。
具體內(nèi)容
先看作者對(duì)這次更新的簡(jiǎn)單描述:

代碼格式化風(fēng)格更新。 重構(gòu)。 構(gòu)建相關(guān)的改動(dòng)。 代碼庫(kù)更新為 TypeScript 編寫(xiě)。
值得一提的是,更換成 TS 之后,生成的代碼體積都有少量的增加,作者猜測(cè)是 TS 加入了一些 runtime 的代碼導(dǎo)致的:

第一個(gè) Commit 中,作者把代碼的類(lèi)型全部改成 .ts,移除文件開(kāi)頭 flow 的標(biāo)記,并且把類(lèi)型的語(yǔ)法全部替換成 TypeScript:

作者用 TS 的 import type 語(yǔ)法重構(gòu)了類(lèi)型導(dǎo)入,我個(gè)人也比較喜歡這樣導(dǎo)入類(lèi)型,更有助于區(qū)分導(dǎo)入的內(nèi)容:

單測(cè)工具的更新,以及 TS 的支持,利用 ts-loader 做編譯:

RollUp 版本的一次大升級(jí):

ESLint 也需要一些改動(dòng),使用 @typescript-eslint/parser,繼承的一些推薦預(yù)設(shè)也改為 @typescript-eslint/eslint-recommended。

CI 中原本 flow 的類(lèi)型檢測(cè),也改成使用 tsc --noEmit 做 TS 的類(lèi)型檢查。

評(píng)價(jià)
可怕的是,這個(gè)如此龐大的 PR 是作者在幾天內(nèi)完成的,這戰(zhàn)斗力簡(jiǎn)直是驚人。
Twitter 的評(píng)論中有人提問(wèn):“把如此巨大的代碼庫(kù)遷移到 TypeScript 需要多長(zhǎng)時(shí)間?”
作者回答:在幾小時(shí)內(nèi)重命名文件,把 flow types 重寫(xiě)成 TS 類(lèi)型并修復(fù)錯(cuò)誤,之后的幾天主要是忙構(gòu)建、測(cè)試相關(guān)的工作。

對(duì)此,外國(guó)推友也表示很震驚:
“你簡(jiǎn)直是個(gè)機(jī)器”:
“他生活的宇宙中,1 小時(shí)可以頂我們 24 小時(shí),或者也可能他是用光速在敲代碼”
CamiloR:“太棒了,很高興核心團(tuán)隊(duì)之外,也有人付出如此多的努力”
Carlos:“我就是核心團(tuán)隊(duì)的成員 ??”
總結(jié)
不得不感嘆,十倍工程師是真實(shí)存在的……這樣一次巨型代碼庫(kù)遷移只花了短短幾天時(shí)間,其實(shí)也體現(xiàn)出作者在 TS 生態(tài)、構(gòu)建以及測(cè)試相關(guān)方面的熟悉程度。
感謝 Vue 核心團(tuán)隊(duì)成員們夸張的戰(zhàn)斗力,給前端界帶來(lái)這么優(yōu)秀的框架而且持續(xù)迭代和優(yōu)化。
Vue 3 雖然是未來(lái),但是 Vue 2 也不會(huì)被放棄,遷移到 TS 以后的 Vue 2 具有更強(qiáng)的代碼可移植性,一定會(huì)綻放出更多精彩。
參考資料
chore: move to typescript 這個(gè) PR: https://github.com/vuejs/vue/pull/12001/commits
[2]Carlos Rodrigues: https://twitter.com/pikax_dev
[3]Vue 2.0 為什么選用 Flow 進(jìn)行靜態(tài)代碼檢查而不是直接使用 TypeScript?: https://www.zhihu.com/question/46397274/answer/101193678
[4]@vue/composition-api plugin: https://github.com/vuejs/composition-api
[5]script setup: https://github.com/vuejs/rfcs/pull/227
[6]非官方插件: https://github.com/underfin/vite-plugin-vue2



