【发布时间】:2016-08-25 21:37:40
【问题描述】:
我喜欢Enlive,但是当我观察到以下内容时,我有些困惑。
考虑以下 Clojure 代码 (also available on github):
(ns enlivetest.core
(:require [net.cgrand.enlive-html :refer [deftemplate defsnippet] :as html]))
(deftemplate page "index.html"
[ctx]
[:.foobar] (html/content (do (println "GENERATING FOOBAR")
"===FOOBAR===")))
这个 HTML 模板(resources/index.html)在这里:
<!DOCTYPE html>
<html>
<body>
</body>
</html>
当调用 page 模板时,我希望它完全忽略其规则的右侧(转换),因为没有与规则选择器 :.foobar 匹配的 HTML 标记。
然而,事实证明,规则的右侧确实得到了评估:
user=> (require '[enlivetest.core :as c])
nil
user=> (c/page {})
GENERATING FOOBAR
GENERATING FOOBAR
("<!DOCTYPE html>\n" "<" "html" ">" "\n " "<" "body" ">" "\n " "</" "body" ">" "\n\n" "</" "html" ">")
(显然,它甚至会被评估两次 - 看起来模板中的每个根 HTML 元素一次)。
但是,尽管没有匹配选择器的元素,为什么还要评估它呢?这是正确的行为吗?我在这里遗漏了什么明显的东西吗?
本示例使用 Enlive 1.1.6,正如其 README 所建议的那样。
非常感谢您的澄清。
编辑#1:
事实证明(感谢@leetwinski),我对事物运作方式的假设是错误的:
我假设 deftemplate 宏只会在规则的选择器与给定 HTML 中的元素匹配时评估规则的右侧(转换部分)。
但正确的是:
规则的右侧将总是在调用定义的模板函数(例如page)期间被评估,并且预计会评估为一个函数,该函数将依次评估为调用时所需的内容(例如,本例中的“===FOOBAR===”)。只有匹配选择器的元素才会调用此函数。
这意味着例如html/content 计算为这样的函数(而不是直接计算所需的内容)。
为了让事情按我最初的预期工作,我可以这样写:
(deftemplate page "index.html"
[ctx]
[:.foobar] #((html/content (do (println "GENERATING FOOBAR")
"===FOOBAR===")) %))
这将导致以下输出:
user=> (c/page {})
("<!DOCTYPE html>\n" "<" "html" ">" "\n " "<" "body" ">" "\n " "</" "body" ">" "\n\n" "</" "html" ">")
或者在向 HTML 模板添加 <div class="foobar"></div> 时:
user=> (c/page {})
GENERATING FOOBAR
("<!DOCTYPE html>\n" "<" "html" ">" "\n " "<" "body" ">" "\n\t\t" "<" "div" " " "class" "=\"" "foobar" "\"" ">" "===FOOBAR===" "</" "div" ">" "\n " "</" "body" ">" "\n\n" "</" "html" ">")
编辑 #2:
已经有几个星期了,但我仍在为如何在 Enlive 中实现这一点而苦苦挣扎。我看到自己一遍又一遍地将规则的转换部分包装成#((html/content ...) %)。
有人解释为什么 Enlive 会评估转换(全部甚至多次),即使它们甚至与当前渲染过程无关?
我可能忽略了一些事情,因为我真的很惊讶这似乎并没有打扰到除了我之外的任何人。
【问题讨论】: