如何看待 svelte 這個前端框架?

看了下。十分精簡。思路也很獨特。


作者是 Rich Harris,也就是 Ractive, Rollup 和 Buble 的作者,堪稱前端界的輪子哥,現在又帶來新輪子了!

這個框架的 API 設計是從 Ractive 那邊傳承過來的(自然跟 Vue 也非常像),但這不是重點。Svelte 的核心思想在於『通過靜態編譯減少框架運行時的代碼量』。舉例來說,當前的框架無論是 React Angular 還是 Vue,不管你怎麼編譯,使用的時候必然需要『引入』框架本身,也就是所謂的運行時 (runtime)。但是用 Svelte 就不一樣,一個 Svelte 組件編譯了以後,所有需要的運行時代碼都包含在裡面了,除了引入這個組件本身,你不需要再額外引入一個所謂的框架運行時!

當然,這不是說 Svelte 沒有運行時,但是出於兩個原因這個代價可以變得很小:

1. Svelte 的編譯風格是將模板編譯為命令式 (imperative) 的原生 DOM 操作。比如這段模板:

&{{ msg }}&

會被編譯成如下代碼:

function renderMainFragment ( root, component, target ) {
var a = document.createElement( "a" );

var text = document.createTextNode( root.msg );
a.appendChild( text );

target.appendChild( a )

return {
update: function ( changed, root ) {
text.data = root.msg;
},

teardown: function ( detach ) {
if ( detach ) a.parentNode.removeChild( a );
}
};
}

可以看到,跟基於 Virtual DOM 的框架相比,這樣的輸出不需要 Virtual DOM 的 diff/patch 操作,自然可以省去大量代碼,同時,性能上也和 vanilla JS 相差無幾(僅就這個簡單示例而言),內存佔用更是極佳。這個思路其實並不是它首創,之前有一個性能爆表的模板引擎 Monkberry.js 也是這樣實現的,ng2 的模板編譯其實也跟這個很類似(但是中間加了一層渲染抽象層)。

2. 對於特定功能,Svelte 依然有對應的運行時代碼,比如組件邏輯,if/else 切換邏輯,循環邏輯等等... 但它在編譯時,如果一個功能沒用到,對應的代碼就根本不會被編譯到結果里去。這就好像用 Babel 的時候沒有用到的功能的 helper 是不會被引入的,又好像用 lodash 或者 RxJS 的時候只選擇性地引入對應的函數。

基於這兩個特點,Svelte 應用的最終代碼量可以非常小。比如它的 TodoMVC min+gzip 之後只有 3kb。

但是,Svelte 也不是沒有它的潛在問題:

1. 雖然在簡單的 demo 裡面代碼量確實非常小,但同樣的組件模板,這樣的 imperative 操作生成的代碼量會比 vdom 渲染函數要大,多個組件中會有很多重複的代碼(雖然 gzip 時候可以緩解,但 parse 和 evaluate 是免不了的)。項目里的組件越多,代碼量的差異就會逐漸縮小。同時,使用的功能越多,Svelte 要包含的運行時代碼也越多,最終在實際生產項目中能有多少尺寸優勢,其實很難說。

2. Svelte 在大型應用中的性能還有待觀察,尤其是在大量動態內容和嵌套組件的情況下。它的更新策略決定了它也需要類似 React 的 shouldComponentUpdate 的機制來防止過度更新。另一方面,其性能優勢比起現在的主流框架並不是質的區別,現在大部分主流框架的性能都可以做到 vanilla js 的 1.2~1.5 倍慢,基於 Virtual DOM 的 Inferno 更是接近原生,證明了 Virtual DOM 這個方向理論上的可能性,所以可以預見以後 web 的性能瓶頸更多是 DOM 本身而不是框架。

3. Svelte 的編譯策略決定了它跟 Virtual DOM 絕緣(渲染函數由於其動態性,無法像模板那樣可以被可靠地靜態分析),也就享受不到 Virtual DOM 帶來的諸多好處,比如基於 render function 的組件的強大抽象能力,基於 vdom 做測試,服務端/原生渲染親和性等等。這一點在我看來比較關鍵。讓我在一點點性能和 Virtual DOM 之間做抉擇的話,我還是會選擇 Virtual DOM。

最後,我個人覺得 Svelte 最具有優勢的地方,就是用來編譯可獨立分發的 Web Components。傳統框架和 Web Components 結合最大的問題就在於運行時:單獨分發的 WC 裡面直接打包框架運行時顯然不現實,不打包的話,又做不到開箱即用。但 Svelte 就沒有這個問題,可以說是最適合這個用例的框架。


不邀自答。

作者Rich-Harris[1],是rollup[2]和ractivejs[3]的作者。

rollup是類似webpack+babel的打包工具,ractivejs則跟這個sveltejs很像

所以看sveltejs的Get started,直接給了compiler方法,這是亮點1,最大的亮點。

他生成的HelloWorld.js不需要依賴如`svelte`這樣的模塊,比如react需要react.js和react-dom.js(編譯到一個js文件也是一樣的),vue需要vue.js,而svelte不需要。相當於依賴了一個0kb的庫(笑。這點倒是和rollup主打的shaking tree很像,畢竟一個人寫的。

可以玩玩他的repl https://svelte.technology/repl

所以說

The magical disappearing UI framework

其他方面:

對比svelte和ractivejs可以發現他們都使用了mustache作為模板,很多寫法幾乎一樣,主要的區別是svelte使用了類似web-component的方案編寫component(組件)。並且給component加上了一下幾個方法。

component.set(POJOData)
component.get(key)
component.observe(key, callback[, options])

component.on(eventName, eventHandle)
component.fire(eventName, event)

component.teardown()

其他沒什麼好說的,主要是on和observe。官方說了,on主要監聽事件(比如用戶操作),而observe主要監聽數據流變化。事件比如觸發了on:select,用戶選擇了下拉菜單的值,數據流則是component.set產生的值的變化,這是亮點2。

另外svelte提供了import Component from "Component.html" 這種寫法以及nested component(嵌套組)件的寫法用於開發複雜應用,這是不算亮點的亮點3。

以下說說個人看法。

1,這是一個類似vue[4]的View層解決方案,但是對於熟悉mustache,handlebars的開發者來說,可以更快速上手地編寫組件。需要學習的只有:

a. 通過on:[event]的語法添加事件,同時事件handle在component的methods里定義(與vue相同),如果是自定義事件,在events里定義(這也算個亮點)

b. 通過bind:[attribute]語法實現雙向綁定

c. 使用大寫字母開頭的標籤名嵌套子組件

d. refs

e. 模板里用到的數據在component的data里定義(與vue相同)

f. 如有遺漏待補充

2,講真,介面比vue更簡潔,可能因為很多功能還在todo吧(逃

3,起步晚,生態環境不容易搭建,雖然ractivejs估計可以兼容進來,但相對於vue,react,生態上差別巨大。尤其是一個號的路由和一個號的數據流控制機

4,其實模板這個東西啊,我個人從mustache到_.templace(JST),再到現在JSX,真的覺得再讓我回過去用我是不願意了,雖然很多人說JSX丑,但是好理解,很順手。mustache算是html為主,JST算是js和html混寫,而jsx是純粹的js,使用xml的語法,這真的很爽。這是品味問題,可能是因為每個人的星座都不一樣吧。我和Rich-Harris肯定不是一個星座。(這個問題歡迎探討)

5,關於on和observe,這也是最近思考得比較多的地方,用戶產生的數據是在組件里消費掉,還是放到data flow里去消費,或者說什麼情況用什麼做法比較好一點。最近比較火的xufei叔離職的問題里有提到view層最好能獨立出來,便於替換其他框架。如果要實現,不就需要把用戶事件在view層之外做處理么,不然每個view層都需要寫一次事件處理。又或許,用戶事件用通用的方法直接轉化(轉化時不做處理)成數據流,再在某個地方進行處理?(想想都不夠簡潔)

最後,推薦嘗試,附官方指南[5]。

參考資料

[1] Rich-Harris (Rich Harris)

[2] rollup/rollup

[3] ractivejs/ractive

[4] vue.js

[5] https://svelte.technology/guide


一覺醒來前端又出新框架了


神奇的 svelte 為我們開啟了一扇新的窗戶,是一套值得推薦在生產場景使用的框架。

先看看性能優勢 :http://svelte-dbmonster.surge.sh/

【生產環境實踐】基於直播流狀態控制的原型UI使用了Svelte

地址: https://github.com/balloob/home-assistant-svelte-prototype

【亮點】

1、 接近Zero Runtime(零依賴)的靜態代碼編譯,這意味著我們可以輕鬆在移動端輕交互場景(如微信應用、HTML5 輕型應用、代替Zepto系絕大部分應用場景等)構建 Single Bundle(單包) 小體積項目。

2、更輕量級友好的API,這決定著我們能夠極短的時間掌握熟悉入手,低成本引入高效率開發項目,不客氣地說,我僅花了不到10分鐘,就能夠可以開始寫代碼了。這正是我最欣賞的API之一。

3、設計了更易接入主流生態的API(如 Redux / Rx / Redux-observable),Component Api可以讓我們很方便通過 set / observe 將數據模型和組件通訊從【視圖解耦】到Redux / Rx / Redux-observable 或者【 單獨設計輕量的交互模型】。但需要對 Props / Observe 的嵌套深度 &> 1 (如數組對象)做好 deep diff 處理。

4、標準化API:約束了 data 的使用範圍,只能通過固定的 API(如 Get / Set / computed 等) 和 template 對 data 操作,避免新人/新手濫用this指針,同時也屏蔽了this指針帶來的副作用。同時使用 Observe 標準化需要被 watch 的狀態,自由度更高,DEBUG變得更輕鬆,不再感到恐慌

5、極簡單的生命周期,僅有 onrender和 teardown,這意味著,很輕鬆的接入主流的插件、框架和庫。利用ref獲取原生dom,可以更輕鬆通過原生解決性能瓶頸問題。現有主流的框架,都有接近8-10個生命周期以應付項目的複雜度,同時也導致在初入框架時,理解life cycle(生命周期)的成本。(例如React的life cycle),其實更多時候我們不需要它們。

6、輕量的template語法:除了配套基本的mustache語法,以及需要區分純函數(即Helper)和非純函數(Methods)。而作者Rich Harris 更推崇單一獨立的組件方式,不推薦由Root組件逐層嵌套的愚蠢笨掘的組件體系。由此產生地組件嵌套層次過深問題的性能問題。更建議直接採用將組件渲染到特定的dom上。

7、還有更多優異的特性,就不細說了,更好的體驗,可以到https://svelte.technology/去了解。

【不足之處】

1、針對需要Multiple Bundle(多包)場景,可能會產生較多的冗餘代碼,但 template語法提供了Helper(純函數),讓我們可以將更多函數抽象到固定的模塊當中Import,從而有效的減少冗餘代碼。畢竟有rollup的tree-shaking和webpack common bundle來抽離出公用模塊。相信後續作者應該會拎出相應的runtime代碼自動合併成runtime code。個人覺得問題不大。

2、推崇獨立組件,意味著至少需要一個輕量級以上的組件通訊管理模型以及抽離交互模型的設計來應付複雜的交互場景,且組件的粒度可能相對會較粗,純獨立組件的思路還是有待觀察的。 過作者還是提供了嵌套組件的方法來緩和這種情況。

3、Component API儘管帶來更多的靈活性和第三方插件調用,但可能會導致開發經驗較淺的新人濫用導致代碼耦合程度較高的情況。

【未來趨勢】

1、組件更標準化、更接近HTML DOM的Template語法

2、更多人會考慮設計數據模型、交互模型來處理DOM,追求Pure View

3、Zero runtime 可能會讓更多的主流框架開發者進行此類改進

總而言之,推薦使用....


目前為止還是觀望比較好。

這個框架最有價值的地方還是提出了各個框架的協作問題。在官Blog上就有說。

Consider interoperability. (omitted...) But if the widget author used Svelte, apps that use it can be built using whatever technology you like. (On the TODO list: a way to convert Svelte components into web components.)

當一個庫作者要發布一個可以給各個框架公用的組件時,不可能加上各個框架的運行時,也就意味著庫作者是不能享受任何框架紅利的。而Svelte卻可以。 沒有運行時的特性也更容易兼容WebComponent。

這是各個框架需要學習一個的地方。

但是從實際上來看,precompile給svelte帶來的困難非常多。以致於可以懷疑它是不是能活到廣泛應用的那天。

首先,precompile斷絕了動態生成component的可能。這在大型項目或特殊情況(比如按照用戶輸入或後台數據生成組件)下還是需要的。而svelte目前必須把編譯器一起送給瀏覽器才能實現。

其次,隨著功能增多,編譯器的實現難度會指數式上升。目前svelte沒有動態載入組件的功能,也沒有類似webcomponent里slot的功能,也沒有scoped slot / structural directive (https://gist.github.com/yyx990803/faebe22e8763f5b17572b35ed96f52fe#scoped-slots, Angular)。這些功能在大型應用中都不可缺少。支持這些功能需要編譯器對模板的作用域進行處理,邊緣條件更多,而且這些特性可以相互嵌套,相互糾纏,導致複雜度指數上升。

最後,svelte如果需要支持如Server Side Rendering, native mobile app這樣的場景。它的代碼生成就需要大改一番。Angular2使用了依賴注入,在不同場景注入不同編譯器實現來達到不同代碼生成。而React/Vue則使用了virtual dom來抽象UI。svelte如果要認真地做框架,生成代碼一定要改。

預編譯這條技術路線對實現有很大挑戰,對用戶使用也有更多限制(主要體現在模板的表達力、debug的便利性、編輯-編譯-反饋的開發工作流上)。即使不看未來,現在svelete的問題也是比較多的,比方生成代碼太臃腫Just to follow up on this. I wrote a little CRUD app (managing a list of users)....,導致了它的一個優點很快就在非玩具項目(不一定大型)中被磨滅。

Rich Harrison以前做的項目,基本都是輪子,Buble這個輪子的bug比較多。Issues · Rich Harris / buble Rollup這個輪子的bug比較多,而且比較惡劣。rollup/rollup。而precompile這個事情比前面的項目相比也不簡單。生成vanilla JavaScript的事情 Google 也做過,和svelete最相近的是GWT, http://www.gwtproject.org/。 GWT沒有流行的一個原因是它的Java出身與JS社區的相性並不好。而Svelte現在沒有增量編譯沒有webpack插件這些硬傷,也給它的推廣帶來困難。希望它不會重蹈前輪覆轍。


Svelte 算是讓 Web 頁面的工作方式回歸到最原始的方法了,當然我說的不是開發方式,這裡我指的是瀏覽器真實在執行的工作。傳統的 Web 開發基本都是 jQuery 的天下,$("xxx") 出來一個元素然後直接對它進行操作,但隨著應用規模的擴大,這種沒有集中狀態管理,只靠粗暴操作 DOM 的方式就行不太通了,架構不好的代碼維護起來也會很費勁。

然後以 Angular.js 1.0 為代表的 MVVM 框架誕生了,緊接著 React.js、Vue.js 等流行框架就如雨後春筍般地出現了。這些框架各有各的特點,以 React.js 舉例,它是一個純粹的 V 層類庫,通過數據到視圖元素的映射來構建界面(即界面是數據的實時體現),想要實現這一目標,最簡單的方法就是直接渲染出 HTML,然後設置到 innerHTML 中。但是這會帶來很嚴重的性能問題,React.js 的解決方法就是使用 VirtualDOM + Diff(現在使用 Fiber Reconciler),這樣通過對比兩個 V-Dom 的差異,然後只將少數差異的部分 patch 到真實 DOM 上,就能極大地提高性能,因此 React.js 一大半的工作都是在如何做 Reconciling 上。當然,使用了 React.js 開發的應用打包出來的代碼,瀏覽器實際執行的代碼,這部分也佔了絕大多數。

當我們需要開發的應用很簡單時,用 React.js 之類的類庫就會有點『殺雞用牛刀』了,這就有點像開發一個記事本桌面應用用 Qt,類庫代碼佔了整個應用的 90%。如果類庫的各個部分不能 cherry-pick 出來的話這利用率就太低了。像 lodash、Ramda、Rx.js 這樣功能模塊相對獨立的類庫 cherry-pick 容易,而 React.js、Angular 這類完整的框架各個內部組件的耦合度很大,基本就要全部打包進去,即使 gzip 也是很大。

再看 Svelte,它的思路很棒,把 HTML 直接編譯成 JavaScript,所有的代碼都是應用會出現的操作,打個比方:

&Hello {{name}}!&

這段代碼包含的唯一操作,也就是有動態性的地方就是 name 這個插值,如果用 React.js 來實現的話,先渲染成 element 然後與 V-Dom 做 diff 操作,然後 patch,bla bla bla,中間還要經過幾個 event loop,是不是很多餘?

Svelte 將這個模板片段 AOT 編譯成很精簡的一段 JavaScript 模塊,這個模塊主要的操作有四個部分:create、mount、update、unmount。create 顧名思義就是創建 HTML 片段,把模板中的各個用 HTML 元素用 Vanilla API 創建出來,然後 mount 就可以將這個片段添加到頁面的 DOM 中。比較重要的就是 update,它是將新數據綁定到視圖的操作,我們看上面這個例子的 update:

function update(changed, state) {
if (changed.name) {
text_1.data = state.name;
}
}

簡潔到無以復加是吧?我們只是想修改一個插值而已,何必大炮打蚊子用 V-Dom 呢?

所以這就是 Svelte 的精髓,用最少的操作(代碼、CPU Cycle)去實現我們的目標,所以它生成的代碼又小運行起來又快。

大家可以自己看看 Svelte compile 後的代碼,很簡潔易懂。當然它現在剛起步,還有很多不足的地方,比如生成的代碼並不是最簡,現有 API 比較少,內建特性也比較少。當然生態也很初步,最大的問題就是缺少合格的路由和狀態管理這樣的類庫,所以現在應用到生產環境的話適用面可能也比較小。不過好在它的親和度比較高,編譯出來的小組件與 React.js 等其他框架結合很輕鬆,所以還是很值得學習和研究的。


這種打包即用的還是挺好的,觀望一下~


文檔寫得真好


可能能成為前端模板界的LLVM,所有運行時的問題都可以想辦法在編譯解決。


yoxjs/yox 還有這個框架也是模仿 ractive 的


推薦閱讀:

React 組件設計思路?
js 變數聲明 函數聲明 變數賦值的實現機制疑惑?
穩妥構造函數模式和工廠模式創建對象有什麼區別?
Vue.js中如何動態的載入、卸載組件?
Google Polymer是前端組件化的未來!那對於現在當下,又該採用什麼技術實現組件化呢?AngularJs可以勝任嗎?

TAG:前端開發 | JavaScript | 前端框架 |