原文: 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. 这是我这个类库的基础, 也完彻底全是高性能的来源.