使用Enlive作为模板引擎

Thu 29 December 2011
  • 手艺 tags:
  • clojure
  • web published: true comments: true

在所有的clojure web开发例子里,对模板的介绍都很少。很多的简单例子都是以hiccup作为页面生成的手段。hiccup是个clojure的html DSL,例子里用这样的DSL生成页面确实很酷,可是他是real world吗,当然不是。

好在clojure世界里早就有了enlive,它不仅是一个通过css selector解析html的库,本身也可以作为模板引擎应用在web开发中。我不知道这种通过css selector的方式是否是enlive首创,不过他实在是非常新颖独特,而且平滑了页面设计和程序的集成。

例如这样一个模板 index.html:
[cc lang="html"]

Sample Text
[/cc]

在clojure程序中,使用enlive的deftemplate
[cc lang="clojure"]
(deftemplate index "index.html"
[ctx]
[:div#cc] (content (:data ctx)))
[/cc]

在控制器里,可以很MVC地渲染页面
[cc lang="clojure"]
(index {:data "rendered text"})
[/cc]

除了content用于渲染文本,还有html-content可以渲染含html标签的内容,以及set-attr用来修改页面元素的属性。

和传统的模板引擎相比,最大的不同是enlive里没有嵌入模板的直观的控制流,没有循环和条件判断,但是并非不可实现。

循环输出一组list

页面 list.html
[cc lang="html"]

[/cc]

定义一个enlive的snippet
[cc lang="clojure"]
(defsnippet item-model "page.html" [:.list-item]
[ctx]
[:.list-item] (content (:data ctx)))
[/cc]

在页面模板里
[cc lang="clojure"]
(deftemplate list-page "list.html"
[ctx]
[:ul#the-list] (content (map item-model (:some-list ctx))))
[/cc]

这样在页面里列表项会被循环输出,而在页面设计时这里可以放任意个li,并且直接交给后台作为模板。

条件判断

页面,设计时显示所有的内容 msg.html
[cc lang="html"] 只在一定条件下显示 [/cc]

在模板中通过clojure的if进行判断
[cc lang="clojure"]
(deftemplate msg "msg.html"
[ctx]
[:span#msg] (if (:show ctx) identity (html-content "")))
[/cc]

解决了这两个问题,基本上用enlive作为模板引擎就没有障碍了。不过enlive也有一点小问题,其一可能是性能的问题,方便的selector显然要比传统的模板语言消耗更多的CPU。另外,在开发过程里,页面文件在服务器启动后不能热加载,修改页面必须重启ring才能看到。也许有时间的话,可以给它加一个reload选项。