原文: http://elm-lang.org/blog/Blazing-Fast-Html.elm
做者: Evan Czaplickicss
新的 elm-html 模塊容許在 Elm 當中直接使用 HTML 和 CSS.
想用 flexbox? 想要沿用已有的樣式表? Elm 如今讓這愉快並且飛快.
好比, 重寫 TodoMVC 應用時, 代碼很是簡單,
初步的性能測試代表對比流行框架來講是很是快的:html
elm-html 和 Mercury 兩個項目都是基於 virtual-dom 項目的,
這就是性能高的緣由. 文章前半部分將介紹 virtual DOM
,
以及說明 putiry(純函數)和不可變性(immutability) 是怎麼提升性能的.
這能夠解釋爲何 Om, Mercury, 還有 Elm 都達到了如此高的性能.node
性能是一個好的收穫, 不過實際的好處是這個方案使得代碼更簡單, 更容易理解和維護.
簡單說, 這使得建立可複用的 HTML 組件和抽象共用模式更容易了.
因此維護大型代碼的人應該對 virtual DOM 的方案保持興趣.git
這個方案對於想要開始用 Elm 的人們來講也是好消息.
就是說用 Elm 同時你也能保留原來的 CSS 還有之前習慣的設計和開發流程.
在項目當中使用 Elm 也更簡單了. 看一下是怎麼工做的.github
這個類庫基於 "virtual DOM" 的想法.
不直接地對 DOM 進行操做, 而是給每一個 frame 構建一個抽象版本的 DOM,
用 node
函數來建立一個簡單的數據結構來表示:編程
node : String -> [Attribute] -> [CssProperty] -> [Html] -> Html
這裏聲明瞭一個標籤, 一組 HTML 屬性, 一組 CSS 屬性, 還有一組子節點.
好比, 用 node
來構建簡單的 profile 組件, 顯示用戶的頭像和名字:數組
profile : User -> Html profile user = node "div" [ "className" := "profile" ] [] [ node "img" [ "src" := user.picture ] [] [] , node "span" [] [] [ text user.name ] ]
注意這裏定義了一個 class, 因此總體咱們能經過 CSS 來定製樣式.
搭配 Elm 的模塊系統, 抽象出通用的模式和重用的代碼就方便了.
你能夠在這裏查看完整的 API 和文檔,
在可複用組件的章節中咱們將看到更多使用的例子.安全
Virtual DOM 聽起來很慢是否是? 每一個 frame 建立一整個新的 scene?
這項技術其實在遊戲產業中普遍使用而且對於 DOM 更新也表現得很好,
其中能夠看到兩種相關的技術: diff 和惰性求值(laziness).數據結構
React 普及了 diff 的方法來判斷 DOM 須要作怎樣的更新.
Diff 意味着在當前的 virtual DOM 和新的 virtual DOM 之間對比出變化量.
這聽起來很奇幻, 實際上就是很簡單的過程.
首先列出全部的差異, 好比某個 <div>
顏色的改變, 或者添加了一個新的.
全部差異找出來之後, 做爲指令對 DOM 進行更新,
這裏經過 requestAnimationFrame 合併到一個大的操做當中.
這裏對 DOM 進行操做的步驟得以完成, 而且一切都是飛快的.
你能夠專心寫代碼, 讓代碼更好懂, 更好維護.架構
這個方案給 Elm 帶來了完美的支持 HTML 和 CSS 的方案!
並且, Elm 對 純函數(purity)和不可變性(immutability)已經有了很好的設施,
這些是對 diff 性能進行優化的關鍵.
根據在 React 和 Om 當中獲得的, 惰性計算和 diff 能夠大幅提高性能,
尤爲是有不可變的數據結構做爲支持的時候.
好比, 渲染一個 tasks 的列表:
todoList : [Task] -> Html todoList tasks = node "div" [] [] (map todoItem tasks)
能夠想見, 其中不少更新, 任務內容是不會改變的. 沒有數據改變, view 也就不用改變.
這時候使用惰性計算就很合適了:
lazy : (a -> Html) -> a -> Html todoWidget : State -> Html todoWidget state = lazy todoList state.tasks
相對於每個 frame 都調用 todoList 的函數,
咱們來判斷 state.tasks
相對上一個 frame 是否有更新.
沒有更新的話, 全部步驟跳過. 沒有任何須要調用函數, 作 diff, 修改 DOM!
這些優化是安全的, 由於函數式純函數, 數據是不可變的.
純函數(purity) 表示 todoList 函數對於相同的輸入老是有相同的計算結果.
因此若是咱們知道 state.tasks
是相同的, 就能夠整個跳過 todoList
執行.
不可變性(Immutability)使得判斷數據"一致"很是簡單.
不可變性保證了當兩個的引用是一致時, 他們從結構上也將是一致的.
因此要肯定 todoList 和 state.tasks
是否跟上一個 frame 一致只要判斷其引用就能夠了.
這個開銷很是小, 若是又是一致, 惰性函數常常能夠省掉大量工做.
這是個很簡單的手法, 卻可以大幅提高性能.
若是在關注 Elm, 你大概能注意到一個模式: 純函數(purity)和不可變性是很重要的.
閱讀 hot-swapping in Elm 和 time traveling debugger 來了解更多.
這個方案讓建立可複用的組件變得很容易了.
好比, 用戶 profile 的列表能夠像這樣漂亮地抽象出來:
import Html (..) profiles : [User] -> Html profiles users = node "div" [] [] (map profile users) profile : User -> Html profile user = node "div" [] [] [ node "img" [ "src" := user.picture ] [] [] , text user.name ]
這樣咱們就用了 profile 組件, 接受一個用戶的數組, 返回 HTML.
這在哪裏都能用, 比模板引擎更好的是, 能夠用 Elm 任意地輔助建立組件.
這樣還能夠開始爲社區建立通用組件和模式.
若是想要建立一些複雜的樣式, 這些也能夠被抽象出來複用.
下面的例子定義了的 font 和 background, 能夠在任意的節點裏混合匹配使用.
-- small reusable CSS properties font : [CssProperty] font = [ "font-family" := "futura, sans-serif" , "color" := "rgb(42, 42, 42)" , "font-size" := "2em" ] background : [CssProperty] background = [ "background-color" := "rgb(245, 245, 245)" ] -- combine them to make individual nodes profiles : [User] -> Html profiles users = node "div" [] (font ++ background) (map profile users)
這樣建立可複用的組件和抽象出通用模式就極爲簡單了, 但咱們還能作更多!
當我在寫 Elm 項目的前身時, HTML 20 歲了, 人們還要看三篇博客五個 StackOverflow 答案,
就爲了搞清楚怎麼讓內容能夠縱向居中.
我最初的目標是完全抽象思考 GUI. 若是咱們能重來, Web 編程會是什麼樣子?
elm-html 對與這個目標出來很大的力.
首先, 它讓 HTML 和 CSS 能被操做, 那麼最新的功能的有點都能被用過來
其次, 它讓創造新的抽象成爲了可能.
這意味着 HTML 和 CSS 成爲了構建更漂亮的抽象的城磚.
好比, 可能經過這個庫來從新構建 Elm 的標籤抽象.
可是更重要的是, 任何人都能試驗新的辦法來讓 view 變得更模塊化, 更愉悅.
Paul Chiusano 在他的 provocative post on CSS 很好得解釋了這種期待.
我對於 Elm 的目標依然是從新思考 Web 編程, 以一種怪異的特殊的方式..
支持 HTML 和 CSS 是這個方向上的一大步. 我對於藉助 elm-html 能作的感到很高興!
正如其餘的方案, 人們可能會問的第一個問題是"在大項目裏看起來怎麼樣?"
這個通用的方案跟用 Om 或者 Facebook 的 Flux 架構大型應用是一碼事.
我不正式地在 how this works in Elm 裏作了概述,
計劃很快會寫正式的文檔和例子出來.
感謝 React 和 Om 發現和推廣這些技術.
特別感謝 Sebastian Markbage, David Nolen, Matt Esch, and Jake Verbaten 幫我理解了他們.
尤爲要謝謝 Matt Esch 和 Jake Verbaten, 他們建立了 virtual-dom 和 mercury. 這是我這個類庫的基礎, 也完徹底全是高性能的來源.