① 編譯型框架 SvelteJS
在2016年,Svelte框架由Rollup等工具的作者Rich Harris推出,旨在解決React等框架存在的問題,雖然它沒有像其他框架那樣大紅大紫,但從State of JS的統計來看,它一直保持著持久的熱度。本文將深入淺出地介紹Svelte,希望更多人在適合的場景下可以考慮使用它。
Svelte的誕生源於Rich對前端庫和框架的深入思考。他非常欣賞React等框架帶來的更聲明式、低心智負擔的開發方式,這使得前端工程師能夠更好地控制復雜度、提升開發效率和質量。然而,他也敏銳地洞察到了一些不那麼盡如人意之處。
Rich注意到,盡管React、Vue等框架引入了Virtual DOM的概念,從而能夠進行現有DOM與期望DOM的對比,並執行較重的DOM操作以保持兩者一致,但這其實更多是一種實現聲明式、狀態驅動UI開發的手段,並非不可或缺。Virtual DOM的主要問題在於有很多不必要的Diff操作。例如,假設我們有一個React App,點擊一次按鈕後,新舊Virtual DOM對比如下:雖然只有p節點下的文本需要更新,但React實際上會遍歷所有節點,造成不必要的額外開銷。盡管人們可能覺得純粹的JS運算不會有顯著的開銷,但在頁面內容和邏輯足夠復雜的情況下,這個問題會變得更加明顯。因此,React提供了useMemo、useCallback等Hooks來避免一些耗時操作的重復執行或不必要的重復渲染,但這些其實可以視為錯誤設計的副作用,讓開發者需要關注一些本應是實現原理層面的技巧,增加了額外的心智負擔。
那麼,是否有一種新的方式,可以在count變化時僅僅更新引用它的DOM文本節點?如果能夠做到這一點,那豈不是更加響應式?
Rich還質疑了運行時在前端框架中的必要性。React、Angular、Vue等框架都有運行時,用於處理用戶操作或數據請求引起組件狀態變化時的高效更新DOM節點。然而,運行時代碼會被打包到最終產物中。以典型的TodoMVC應用為例,我們來看幾種方案下的JS包體積:如果我們以JavaScript ES6為基準衡量其他方案因為運行時而帶來的額外包體積,那麼Vue大約是10倍,React約為18倍,Angular則接近27倍。誠然,這些優秀的框架為開發者提供了更為便捷和高效的開發方式,而且現在動輒上兆的中大型前端應用比比皆是,幾十K壓縮後的JS包並不是那麼關鍵。不過,退一步想,如果有一個新的方案可以提供與主流框架相媲美的特性,同時最終包體積接近原生JS,那豈不是更好?
基於以上兩點洞見,Rich開始從一個全新的角度理解和思考前端框架:前端框架應該是主要用來結構化想法而非代碼,它們使得應用的復雜度能夠受到合理的控制。如果我們有辦法在提供結構化想法的種種功能的同時,在編譯階段將應用源代碼轉換為接近原生JS實現的最終代碼,那麼就不再需要瀏覽器和源代碼之間的中間抽象層,我們的應用也會運行得更快。
雖然有了一個嶄新的思路,但落實在實現層面還會有眾多挑戰。接下來,我們將簡要介紹Svelte的具體實現。
Svelte採用了模板為先的組件封裝方案,每個組件都存儲在.svelte文件中,語法是HTML的超集。一個示例將前面的計數器App用Svelte重新實現,看起來非常像Vue,組件的JS、CSS、HTML都在同一文件中集中管理。因此,從開發者的角度來看,Svelte的編程方式十分熟悉、自然。選擇模板而非靈活的JSX的原因也體現在這個例子中,編譯器很容易發現結構化的視圖中p元素有一部分文本需要使用count,以及點擊按鈕時需要調用一個回調函數,其他部分都是確定的。因此,只需在count變化時刷新相應的DOM文本節點即可。為了在有一定約束的情況下提供足夠多的功能,Svelte也提供了一系列模板語法。
在介紹Svelte編譯器如何工作之前,我們不妨設想:如果讓我們自己使用原生JS實現上面的計數器App的話,會是什麼樣的呢?一個可供參考的實現如下(暫時忽略CSS)。
Svelte生成的JS結果如下,可以運行的完整示例見NaiveCompiledApp。可以看出,雖然細節上有些許不同,但是整體的代碼結構與我們前面人肉編譯的版本非常相像。令人驚嘆的是,Svelte也能在count變化時精準地更新對應的文本節點,十分高效。這里的回調函數中的$$invalidate可以理解成一種更具效率的數據變更處理機制,具體實現運用了位掩碼(bitmask)來標記臟數據的變數索引,然後安排一個microtask非同步進行批量更新。
此外,除了一些簡單的append、element、set_data等工具方法和SvelteComponent基類以外,最終構建的應用幾乎沒有使用任何額外的框架代碼,所有的框架工作都基本在編譯階段完成,這真所謂是「編譯器即框架」!
在編譯器內部,可以細分為解析模板、分析代碼、構建產物三個步驟。在編譯過程中,Svelte將.svelte文件編譯成一個JS文件和一個可選的CSS文件,這些產物可以在瀏覽器中直接運行。
在介紹完基本原理後,讓我們看下Svelte的具體表現。從生成最終代碼的體積來看,同樣以TodoMVC為例,Svelte代碼的最終體積是:JS包13K,Gzip壓縮後5.3K。以下是與其他框架對照的圖表。Svelte構建JS包的體積是Vue的1/6,React的1/9,Angular的1/12,已經非常接近於原生JS。可以說是非常精巧的。
從運行速度來看,JSFrameworkBenchmark的測試結果如下。從最後一行的加權結果來看,Svelte的性能非常接近原生JS,與同類型編譯型框架Solid以及以快著稱的類React框架Inferno相當,優於Vue、Angular,更是大幅領先React。
了解完基本原理後,我們將介紹幾個常用的進階特性,如顯式聲明響應性。在實際開發中,我們常常遇到一些場景:一個變數依賴了另一個或多個變數,且這種依賴關系非常確定。例如:
得益於編譯器即框架的優勢,Svelte使用$標簽來更為顯式地聲明這類依賴關系,建立更具效率的響應性:依賴的變數有變化則需要重新執行賦值操作。下面是一個示例。
實現這一特性的思路並不復雜:遇到有標簽$的賦值語句時,則在等號右側變數變化的時候執行$$invalidate將左側變數標記為臟數據,進而觸發後續的更新邏輯。進一步地,Svelte還支持使用$標簽來聲明任意變數的依賴關系。
② C語言編譯器codeblocks在代碼編寫框下面有個編譯信息提示框,我不小心把它叉掉了,請問如何把
去上面的功能條裡面找,說不定有個界面選項,然後裡面有個叫編譯框的,然後勾上就可以了