在所有的clojure web开发例子里,对模板的介绍都很少。很多的简单例子都是以hiccup作为页面生成的手段。hiccup是个clojure的html DSL,例子里用这样的DSL生成页面确实很酷,可是他是real world吗,当然不是。
好在clojure世界里早就有了enlive,它不仅是一个通过css selector解析html的库,本身也可以作为模板引擎应用在web开发中。我不知道这种通过css selector的方式是否是enlive首创,不过他实在是非常新颖独特,而且平滑了页面设计和程序的集成。
例如这样一个模板 index.html:
在clojure程序中,使用enlive的deftemplate
(deftemplate index "index.html"
[ctx]
[:div#cc] (content (:data ctx)))
[ctx]
[:div#cc] (content (:data ctx)))
在控制器里,可以很MVC地渲染页面
(index {:data "rendered text"})
除了content用于渲染文本,还有html-content可以渲染含html标签的内容,以及set-attr用来修改页面元素的属性。
和传统的模板引擎相比,最大的不同是enlive里没有嵌入模板的直观的控制流,没有循环和条件判断,但是并非不可实现。
循环输出一组list
页面 list.html
定义一个enlive的snippet
(defsnippet item-model "page.html" [:.list-item]
[ctx]
[:.list-item] (content (:data ctx)))
[ctx]
[:.list-item] (content (:data ctx)))
在页面模板里
(deftemplate list-page "list.html"
[ctx]
[:ul#the-list] (content (map item-model (:some-list ctx))))
[ctx]
[:ul#the-list] (content (map item-model (:some-list ctx))))
这样在页面里列表项会被循环输出,而在页面设计时这里可以放任意个li,并且直接交给后台作为模板。
条件判断
页面,设计时显示所有的内容 msg.html
在模板中通过clojure的if进行判断
(deftemplate msg "msg.html"
[ctx]
[:span#msg] (if (:show ctx) identity (html-content "")))
[ctx]
[:span#msg] (if (:show ctx) identity (html-content "")))
解决了这两个问题,基本上用enlive作为模板引擎就没有障碍了。不过enlive也有一点小问题,其一可能是性能的问题,方便的selector显然要比传统的模板语言消耗更多的CPU。另外,在开发过程里,页面文件在服务器启动后不能热加载,修改页面必须重启ring才能看到。也许有时间的话,可以给它加一个reload选项。