HTML也可以靜態編譯?

ThoughtWorks發表於2016-11-30

More than React系列文章:

More than React(一)為什麼ReactJS不適合複雜的前端專案?

More than React(二)React.Component損害了複用性?

More than React(三)虛擬DOM已死?

More than React(四)HTML也可以靜態編譯?


《More than React》系列的上一篇文章《虛擬DOM已死?》比較了Binding.scala和其他框架的渲染機制。本篇文章中將介紹Binding.scala中的XHTML語法。

其他前端框架的問題

對HTML的殘缺支援

以前我們使用其他前端框架,比如Cycle.js 、Widok、ScalaTags時,由於框架不支援 HTML語法,前端工程師被迫浪費大量時間,手動把HTML改寫成程式碼,然後慢慢除錯。

就算是支援HTML語法的框架,比如ReactJS,支援狀況也很殘缺不全。

比如,在ReactJS中,你不能這樣寫:

前端工程師必須手動把 classfor 屬性替換成 classNamehtmlFor,還要把內聯的 style 樣式從CSS語法改成JSON語法,程式碼才能執行:

這種開發方式下,前端工程師雖然可以把HTML原型複製貼上到程式碼中,但還需要大量改造才能實際執行。比Cycle.js、Widok或者ScalaTags省不了太多事。

不相容原生DOM操作

此外,ReactJS等一些前端框架,會生成虛擬DOM。虛擬DOM無法相容瀏覽器原生的DOM API ,導致和jQuery、D3等其他庫協作時困難重重。比如ReactJS更新DOM物件時常常會破壞掉jQuery控制元件。

Reddit很多人討論了這個問題。他們沒有辦法,只能棄用jQuery。我司的某客戶在用了ReactJS後也被迫用ReactJS重寫了大量jQeury控制元件。

Binding.scala中的XHTML

現在有了Binding.scala ,可以在@dom方法中,直接編寫XHTML。比如:

以上程式碼會被編譯,直接建立真實的DOM物件,而沒有虛擬DOM。

Binding.scala對瀏覽器原生DOM的支援很好,你可以在這些DOM物件上呼叫DOM API,與 D3、jQuery等其他庫互動也完全沒有問題。

ReactJS對XHTML語法的殘缺不全。相比之下,Binding.scala支援完整的XHTML語法,前端工程師可以直接把設計好的HTML原型複製貼上到程式碼中,整個網站就可以執行了。

Binding.scala中XHTML的型別

@dom方法中XHTML物件的型別是Node的派生類。

比如,<div></div> 的型別就是HTMLDivElement,而 <button></button> 的型別就是 HTMLButtonElement

此外, @dom 註解會修改整個方法的返回值,包裝成一個Binding

注意typedButton是個原生的HTMLButtonElement,所以可以直接對它呼叫 DOM API。比如:

這段程式碼中,typedButton.bind.innerHTML 呼叫了 DOM API HTMLButtonElement.innerHTML。通過autoPrintln.watch(),每當按鈕發生更新,autoPrintln中的程式碼就會執行一次。

其他HTML節點

Binding.scala支援HTML註釋:

Binding.scala也支援CDATA塊:

內嵌Scala程式碼

除了可以把XHTML內嵌在Scala程式碼中的 @dom 方法中,Binding.scala 還支援用 { ... } 語法把 Scala 程式碼內嵌到XHTML中。比如:

XHTML中內嵌的Scala程式碼可以用 .bind 繫結變數或者呼叫其他 @dom 方法,比如:

上述程式碼渲染出的網頁中,時間會動態改變。

強型別的 XHTML

Binding.scala中的XHTML 都支援靜態型別檢查。比如:

由於以上程式碼有拼寫錯誤,編譯器就會報錯:

內聯CSS屬性

style 屬性設定內聯樣式時,style 的值是個字串。比如:

以上程式碼中設定的 typoStyleName 樣式名寫錯了,但編譯器並沒有報錯。

要想讓編譯器能檢查內聯樣式,可以用 style: 字首而不用 style 屬性。比如:

那麼編譯器就會報錯:

這樣一來,可以在編寫程式碼時就知道屬性有沒有寫對。不像原生JavaScript / HTML / CSS那樣,遇到bug也查不出來。

自定義屬性

如果你需要繞開對屬性的型別檢查,以便為HTML元素新增定製資料,你可以屬性加上 data: 字首,比如:

這樣一來Scala編譯器就不會報錯了。

結論

本文的完整DEMO請訪問 ScalaFiddle

從這些示例可以看出,Binding.scala 一方面支援完整的XHTML ,可以從高保真HTML 原型無縫移植到動態網頁中,開發過程極為順暢。另一方面,Binding.scala 可以在編譯時靜態檢查XHTML中出現語法錯誤和語義錯誤,從而避免bug 。

以下表格對比了ReactJS和Binding.scala對HTML語法的支援程度:

ReactJS Binding.scala
是否支援HTML語法? 殘缺支援 完整支援
是否支援標準的style屬性? 不支援,必須改用 JSON 語法 支援,既支援標準的style屬性也支援style:字首
是否支援標準的class屬性? 不支援,必須改用className 支援,既支援class也支援className
是否支援標準的for屬性? 不支援,必須改用htmlFor 支援,既支援for也支援htmlFor
是否支援HTML註釋? 不支援 支援
是否相容原生DOM操作? 不相容 相容
是否相容jQuery? 不相容 相容
能否在編譯時檢查出錯誤? 不能

我將在下一篇文章中介紹 Binding.scala 如何實現伺服器傳送請求並在頁面顯示結果的流程。

相關連結

相關文章