【譯】Vue 3 設計歷程(尤玉溪)

重寫Vue.js的下一個主要版本的經驗教訓。

在過去的一年中,Vue團隊一直在研究Vue.js的下一個主要版本,咱們但願在2020年上半年發佈該版本。(在撰寫本文時,這項工做仍在進行中)。 Vue的主要版本於2018年末造成,當時Vue 2的代碼庫已有兩年半的歷史了。在通用軟件的生命週期中聽起來可能並不長,但在此期間,前端環境發生了巨大變化。前端

有兩個主要的考慮因素使咱們開發了Vue的新主要版本(並重寫了它):首先,主流瀏覽器廣泛提供了新的JavaScript語言功能。其次,隨着時間的推移,當前代碼庫中的設計和體系結構問題已經暴露出來。vue

爲何要改寫

利用新的語言功能

隨着ES2 015 的標準化,JavaScript (正式稱爲ECMASc ript,縮寫爲ES )得到了重大改進,主流瀏覽器終於​​開始爲這些新功能提供不錯的支持。特別是一些爲咱們提供了極大提升Vue功能的機會。算法

其中最值得注意的是Proxy,它容許框架攔截對象上的操做。Vue的核心功能是可以偵聽對用戶定義狀態所作的更改並以反應方式更新DOM的能力。Vue 2經過使用getter和setter替換狀態對象上的屬性來實現這種反應性。切換到代理將使咱們消除Vue的現有限制,例如沒法檢測到新的屬性添加並提供更好的性能。後端

可是,代理是本機語言功能,不能在舊版瀏覽器中徹底填充。爲了利用它,咱們知道咱們必須調整框架的瀏覽器支持範圍,這是一個重大突破,只能在新的主要版本中發佈。數組

解決架構問題

要在當前的代碼庫中解決這些問題,將須要進行大量風險較大的重構,這幾乎等同於rewrite 。 在維護Vue 2的過程當中,因爲現有架構的侷限性,咱們積累了許多難以解決的問題。例如,模板編譯器的編寫方式使適當的源映射支持很是具備挑戰性。一樣,雖然Vue 2從技術上容許構建針對非DOM平臺的更高級別的渲染器,但咱們必須派生代碼庫並複製大量代碼,才能實現這一點。要在當前的代碼庫中解決這些問題,將須要進行大量風險較大的重構,這幾乎等同於rewrite 。瀏覽器

同時,咱們以各類模塊的內部與浮動代碼之間隱式耦合的形式積累了技術債務,而浮動代碼彷佛並不屬於任何地方。這使得孤立地理解代碼庫的一部分變得更加困難,而且咱們注意到,貢獻者不多會對進行重要的更改充滿信心。重寫將使咱們有機會牢記這些注意事項來從新考慮代碼組織。前端框架

初始原型階段

咱們於2018年末開始對Vue 3進行原型設計,其初步目標是驗證這些問題的解決方案。在此階段,咱們主要致力於爲進一步發展奠基堅實的基礎。架構

切換到TYPESCRIPT

Vue 2最初是用純ES編寫的。在原型開發階段以後不久,咱們意識到類型系統對於這種規模的項目將很是有幫助。類型檢查極大地減小了在重構期間引入意外錯誤的機會,並有助於貢獻者更自信地進行重要的更改。咱們經過Facebook的流量類型檢查,由於它能夠逐步添加到現有的平紋ES項目。流量在必定程度上有所幫助,可是咱們沒有從但願中得到太多收益。特別是不斷變化的變化使升級變得很痛苦。與TypeScript與Visual Studio Code的深度集成相比,對集成開發環境的支持也不理想。框架

咱們還注意到,用戶愈來愈多地同時使用Vue和TypeScript。爲了支持它們的用例,咱們必須與使用不一樣類型系統的源代碼分開創做和維護TypeScript聲明。切換到TypeScript將使咱們可以自動生成聲明文件,從而減輕了維護負擔。frontend

解耦內部封裝

咱們還採用了monorepo設置,其中的框架由內部軟件包組成,每一個內部軟件包都具備本身的單獨API,類型定義和測試。咱們但願使這些模塊之間的依賴關係更加明確,從而使開發人員更容易閱讀,理解並進行全部更改。這是咱們努力下降項目貢獻壁壘並提升其長期可維護性的關鍵。

設置RFC流程

到2018年末,咱們有了一個使用新的反應性系統和虛擬DOM渲染器的工做原型。咱們已經驗證了咱們想要進行的內部體系結構改進,可是隻包含了面向公衆的API更改的草稿。如今是將它們變成具體設計的時候了。

咱們知道咱們必須儘早而仔細地作到這一點。Vue的普遍使用意味着突破性變化可能致使用戶大量遷移成本和潛在的生態系統碎片化。爲了確保用戶可以提供有關重大更改的反饋,咱們在2019年初採用了RFC(徵求意見)流程。每一個RFC遵循一個模板,其中側重於動機,設計細節,權衡和採用策略。因爲此過程是在GitHub存儲庫中進行的,提案是做爲請求請求提交的,所以討論會在註釋中天然展開。

該RFC的過程已經證實了極大的幫助,做爲一個思想框架,它迫使咱們要充分考慮潛在變化的方方面面,讓咱們的社區參與設計過程,並提交深思熟慮出來的功能要求。

更快更小

性能對於前端框架相當重要。儘管Vue 2具備出色的性能,但經過嘗試新的渲染策略,重寫提供了進一步發展的機會。

克服虛擬DOM的瓶頸

Vue有一個至關獨特的呈現策略:它提供相似於HTML的模板語法,但將模板編譯爲可返回虛擬DOM樹的呈現函數。該框架經過遞歸遍歷兩個虛擬DOM樹並比較每一個節點上的每一個屬性來肯定實際DOM的哪些部分須要更新。因爲現代JavaScript引擎執行了高級優化,所以這種有點蠻力的算法一般很快,可是更新仍然涉及許多沒必要要的CPU工做。當您查看包含大量靜態內容且只有少許動態綁定(整個虛擬DOM)的模板時,效率低下尤爲明顯仍然須要遞歸地行走到樹上,以瞭解發生了什麼變化。

幸運的是,模板編譯步驟使咱們有機會對模板進行靜態分析並提取有關動態零件的信息。Vue 2經過跳過靜態子樹在某種程度上作到了這一點,可是因爲過於簡單的編譯器體系結構,難以實施更高級的優化。在Vue 3中,咱們使用適當的AST轉換管道重寫了編譯器,這使咱們可以以轉換插件的形式編寫編譯時優化。

有了新的體系結構,咱們但願找到一種渲染策略,以儘量減小開銷。一種選擇是放棄虛擬DOM並直接生成命令式DOM操做,但這將消除直接編寫虛擬DOM渲染功能的能力,咱們發現這對高級用戶和庫做者很是有價值。另外,這將是一個巨大的突破性變化。

其次,最好的方法是消除沒必要要的虛擬DOM樹遍歷和屬性比較,這在更新過程當中每每會帶來最大的性能開銷。爲了實現這一點,編譯器和運行時須要協同工做:編譯器分析模板並生成帶有優化提示的代碼,而運行時將拾取提示並在可能的狀況下采用快速路徑。這裏有三個主要的優化工做:

首先,在樹的層面上,咱們注意到,節點結構在沒有模板指令動態改變節點結構的留徹底靜態的(例如,v-if和v-for)。若是咱們將模板分爲嵌套由這些結構指令分隔的「塊」,每一個塊內的節點結構再次變得徹底靜態。當咱們更新一個塊內的節點時,咱們再也不須要遞歸遍歷樹-能夠在平面數組中跟蹤該塊內的動態綁定。經過將咱們須要執行的樹遍歷量減小一個數量級,此優化可避免虛擬DOM的大部分開銷。\

其次,編譯器會主動檢測模板中的靜態節點,子樹甚至數據對象,並將其提高到生成代碼中的render函數以外。這樣能夠避免在每一個渲染上從新建立這些對象,從而大大提升了內存使用率並減小了垃圾回收的頻率。\

第三,在元素級別,編譯器還會根據須要執行的更新類型爲具備動態綁定的每一個元素生成一個優化標誌。例如,具備動態類綁定和許多靜態屬性的元素將收到一個標誌,指示僅須要進行類檢查。運行時將獲取這些提示並採用專用的快速路徑。

CPU時間,即執行JavaScript計算所花費的時間,不包括瀏覽器 DOM 操做。

綜上所述,這些技術已顯著提升了咱們的渲染更新的基準,與Vue公司3有時須要不到2 Vue公司的第十CPU 時間。

最小化包裝尺寸

框架的大小也會影響其性能。這是Web應用程序的惟一關注點,由於須要動態下載資產,而且在瀏覽器解析必要的JavaScript以前,該應用程序將是交互式的。對於單頁應用程序尤爲如此。儘管Vue一直是相對輕量級的( Vue 2的運行時大小壓縮爲23 KB),但咱們注意到了兩個問題:

首先,並非每一個人都使用框架的全部功能。例如,從未使用過渡功能的應用仍需支付與過渡相關的代碼的下載和解析費用。

其次,當咱們添加新功能時,該框架會無限期地增加。當咱們考慮新功能添加的折衷時,這使束的尺寸不成比例。所以,咱們傾向於僅包含將由大多數用戶使用的功能。

理想狀況下,用戶應該可以在構建時刪除未使用的框架功能的代碼-也稱爲「風雨飄搖」 -只爲他們使用的東西付費。這也將使咱們可以發佈一部分用戶會以爲有用的功能,而不會增長其他用戶的有效負載成本。

在Vue 3中,咱們經過將大多數全局API和內部幫助程序移至ES模塊導出來實現了這一目標。這使現代的捆綁器能夠靜態分析模塊依賴性並刪除與未使用的導出相關的代碼。模板編譯器還會生成搖樹友好的代碼,若是該功能實際上在模板中使用,則該代碼僅導入該功能的幫助程序。

框架的某些部分永遠不會搖晃,由於它們對於任何類型的應用程序都是必不可少的。咱們將這些必不可少的部分的度量標準稱爲基準尺寸。儘管增長了許多新功能,但Vue 3的基準大小大約壓縮了10 KB ,不到Vue 2的一半。

知足規模需求

咱們還想提升Vue處理大型應用程序的能力。咱們最初的Vue設計着重於下降進入障礙和溫和的學習曲線。可是隨着Vue愈來愈普遍地被採用,咱們瞭解了更多有關項目需求的信息,這些項目包含數百個模塊,而且隨着時間的流逝由數十名開發人員維護。對於這些類型的項目,像TypeScript這樣的類型系統以及乾淨組織可重用代碼的能力相當重要,而Vue 2在這些領域的支持並不理想。

在設計Vue 3的早期階段,咱們嘗試經過提供對使用類編寫組件的內置支持來改善TypeScript集成。挑戰在於,咱們須要使類可用的許多語言功能(如類字段和裝飾器)還是提案,而且在正式成爲JavaScript一部分以前可能會發生變化。涉及到的複雜性和不肯定性使咱們懷疑添加Class API是否真的合理,由於它除了提供更好的TypeScript集成以外沒有提供任何其餘功能。

咱們決定研究其餘解決擴展問題的方法。受React Hooks的啓發,咱們考慮過公開較低級別的反應性和組件生命週期API,以實現一種更自由形式的編寫組件邏輯的方式,稱爲Composition API。無需經過指定一長串選項來定義組件,Composition API容許用戶像編寫函數同樣自由地表達,編寫和重用有狀態組件邏輯,同時提供出色的TypeScript支持。

咱們對這個想法感到很是興奮。儘管Composition API旨在解決特定類別的問題,但從技術上講,僅在編寫組件時纔可使用它。在該提案的初稿中,咱們有所領先,並暗示咱們可能會在未來的版本中將現有的Options API替換爲Composition API。這致使社區成員的大量反擊,這爲咱們提供了寶貴的課程,可使他們清楚地傳達長期計劃和意圖,以及瞭解用戶的需求。在聽取了咱們社區的反饋以後,咱們對提案進行了徹底的從新設計,從而明確代表Composition API將是對Options的補充和補充API。收到修改後的提案更加積極,並收到了許多建設性的建議。

尋求平衡

開發人員資料的多樣性與用例的多樣性相對應 在Vue超過一百萬的開發人員中,只有HTML/CSS的基礎知識的初學者,從jQuery遷移的專業人員,從另外一個框架遷移的退伍軍人,正在尋找前端解決方案的後端工程師以及大規模處理軟件的軟件架構師。開發人員配置文件的多樣性與用例的多樣性相對應:一些開發人員可能但願在舊版應用程序上增長交互性,而另外一些開發人員可能須要快速週轉但維護需求有限的一次性項目。在項目的整個生命週期中,建築師可能不得不處理大型的,多年期的項目和一個波動的開發團隊。

在咱們尋求平衡各類折衷方案的同時,Vue的設計不斷受到這些需求的影響和啓發。Vue的口號,「漸進框架」封裝了由此過程產生的分層API設計。初學者可使用CDN腳本,基於HTML的模板和直觀的Options API來輕鬆學習,而專家可使用功能齊全的CLI,渲染功能和Composition API來處理雄心勃勃的用例。

要實現咱們的願景,還有許多工做要作-最重要的是,更新支持庫,文檔和工具以確保順利遷移。在接下來的幾個月中,咱們將繼續努力,咱們火燒眉毛地想看看社區將經過Vue 3創造什麼。

翻譯自increment 英文原版地址

相關文章
相關標籤/搜索