尤雨溪迴應:Vue與TypeScript爲什麼相性特別差?

近日,有開發者在知乎上提出了一個問題:“TypeScript 不適合在 vue 業務開發中使用嗎?”,Vue的作者尤雨溪針對這一問題發表了自己的看法,也解釋了Vue 3.0選用TypeScript的原因,全文如下。

注:本文已獲得尤雨溪本人授權轉載。

必須要承認的是,2.x 的 TS 支持顯然跟 React 和 Angular 是有差距的,這也是爲什麼 3.0 要加強這一塊。

關於目前 2.x 跟 TS 的整合,通常需要基於 vue-class-component 來用基於 class 的組件書寫方式。雖然對於默認的對象書寫方式有類型推導支持,但裏面的實現繞了很多彎。問題的本質其實很簡單:因爲當初 API 的設計根本就沒有考慮類型系統。

Vue 五年半以前剛開始寫的時候,JS 根本沒有類型系統,我自己當時也不用帶類型的語言,所以 Vue 的組件本質上就是一個 “包含了描述組件選項的對象”。

這個設計算是一個典型的 intuition based design,它不是從語言本身的機制或是類型系統出發去設計的,而是單純從人如何描述自己想要的東西出發的。這樣的設計的好處是對於新手很容易理解,缺陷是跟類型系統之間確實存在一定的 “斷層”。這個斷層的意義到底有多大,對於不同類型的用戶其實不太一樣。首先,因爲 API 還算簡單直白,所以即使和傳統的 OO 有一定的 mismatch,但大部分人適應起來還是很快。對於不使用類型系統,更關注組件業務邏輯的用戶來說,這個斷層其實是感受不到的。

其次,這裏的斷層其實是雙向的:你可以說是 Vue 的 API 設計不 type friendly,反過來也可以說是 TS 的類型系統不夠強大所以沒法給 Vue 提供足夠好的支持。一個例子就是 JSX 其實一開始也沒有類型支持,完全是 TS 強行給加了一整套針對 JSX 的推導機制纔給了 TSX 現在的開發體驗。如果 TS 因爲 JSX 不屬於真正的 JS 規範因而不提供支持,是不是也可以說 React 的設計跟類型系統存在斷層?

當然想要抹平這個斷層,更直接的辦法是對 API 進行重新設計。這話說起來很簡單,但對於 Vue 來說,改 API 需要考慮很多東西:

  • 跟原有 API 的兼容性:能否同時支持新舊 API?舊的用戶如何升級?像 Angular 那樣直接改得面目全非當然比較簡單,但說直接點就是不管舊版本用戶的死活;

  • 如何設計出既能提供良好的類型推導,又不讓爲了類型而做的設計 leak 到非 TS 用戶的使用體驗中去?如何在 TS 和非 TS 的使用體驗之間做到最好的一致性和平衡?Again,像 Angular 那樣不管非 TS 的用戶的死活當然也是比較簡單,但我們不會這麼做。

  • class 的原生支持不理想:如果太早用 class 作爲默認的 API,那麼意味着用戶不預編譯就無法在一部分瀏覽器中使用,這是我們一直極力避免的情況。Vue 一直很重視 “引入一個 script 就可以開始寫” 這樣的用例,因爲這保證了新人上手的最低門檻,也讓 Vue 可以很簡單的整合到 legacy 應用當中作爲 jQuery 的替代品。所以要改 API 有一個時機問題,太早了不合適。

  • 用 class 寫組件所依賴的 class fields, decorators 等提案尚不穩定:基於 stage<4 的提案設計 API 是非常具有風險的,比如大家一開始都默認 class fields 的語義是 [[Set]],結果到 stage 3 改成了 [[Define]],這下 Polymer 急死了,因爲它們有一個 API 設計是完全依賴了 class fields 的 [[Set]] 語義的。decorators 提案被徹底重新設計,並且在最近的 TC39 試圖升 stage 3 被 V8 團隊出於性能的考慮否決,可能又要大改;decorators 是否能放在 export 前面依然還在爭論,爲此 Angular 的老大 Igor 急的不行… 所以,如何避免設計出來的 API 在提案發生改動之後被迫也要改,陷自己於尷尬境地?Again,時機問題,等提案成熟,或是等有爭議的點有結論後再動手(雖說我其實也是被邀請參與了這些東西的討論的,但討論規範真的是超級費時費神,還往往說服不了幾個人…)。

額外說說模版和類型推導的關係。表面上看,隔了一層模版語法 + 編譯,似乎確實存在 “斷層”,但其實裏面沒你想的差那麼遠。Vue 的模版是編譯成 virtual dom 渲染函數的,生成的 js 跟 React 的渲染函數一樣可以類型推導,而模版跟生成的 js 之間是完整的邏輯映射,所以這裏其實主要是需要做一些工具鏈上的銜接,把對生成的 js 分析出來的 intellisense 反饋到 IDE 裏的模版上,技術上是完全可行的。

高票答案有另一個地方說 React 的 js 本位使得它做多端渲染簡單,這話其實不對,做多端本質上是需要運行時一開始就做核心和平臺分離的設計,這一點 Vue 2 一開始就做了(所以纔會有 weex, NativeScript Vue 等等),實際上有模版這個更簡單的編譯源,做多端靈活性還更高一點,現在用 Vue 做小程序多端的方案一點都不少啊,mpvue, megalo, uni-app…

至於 Vue 本身使用什麼類型系統做內部實現,其實是另一個問題了。用戶用 TS 寫 Vue 的體驗關鍵還是在 API,用 TS 做內部實現其實只是減少了額外維護 d.ts 的成本。

至於當初爲什麼沒用 TS,我之前的回答相信很多人都看過了,誰能想到 Flow 團隊會這麼爛尾呢。相比之下,TS 團隊確實是在用心做事的。

總結一下:

  • 現有的 API 和類型系統的結合存在缺陷,屬於歷史遺留;改新的 API 有個時機問題,請耐心等待 Vue 3;

  • TSX 類型支持好是因爲 TS 專門開了後門給做了支持;模版只要工具鏈到位一樣可以做到;

  • Vue 2 一開始內部實現就有類型系統,但是沒想到 Flow 爛尾了,而 TS 整個生態越做越好。

更多內容,請關注前端之巔。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章