我的博客生成器是用Scala做的,既然是“生成器”,那就少不了模板,少不了模板引擎。
我在最开始的时候考虑了两个Scala的模板引擎:
最开始对Scalate非常看好,因为印象中它是一个比较早也比较出名的Scala模板引擎,提供了多种语法,也有貌似很详细的文档。然而当我想项目中使用它的时候,惊讶地发现,它不知道什么时候变成了一个web framework。虽然提供了详细的语法文档,却找不到任何在普通Scala项目中使用它的办法!
Twirl
是将Playframework2.x内置的scala模板引擎,抽取出来单独发布了。twirl
是@
的一种发音,而该符号在模板中大量使用。它在文档中介绍了如何通过sbt插件来使用它,但是那时候我对SBT还不太熟悉,不知道如何下手,所以也没有采用。
最后选择的是一个Java版的mustache框架:mustachejava
但是在后来的使用中发现了一些问题:
utf8
,则读取中文文件会变问号。原因是里面有一些读取文件的代码,没有指定字符集。见我提的Issue最近实在无法忍受,决定把它换成Twirl
。现在已经成功了,因为你看到的这稿博客就是它生成的。
使用方法比较简单,直接照着它的readme来就行了,这里记录一些需要注意的东西。
注意文件名形如{name}.scala.{ext}
,中间一定要有一个scala
,后缀是html
,js
, xml
,或者txt
之一。
我开始的时候忘了写scala
,结果怎么都出不来,反复检查之后才发现,一定要注意。
Twirl
的模板需要编译成scala源代码,以一个object
的方式供其它Scala代码使用。在Play中,这是自动的,但是对于普通Scala项目,我们必须手动操作。
在Sbt命令中行执行:
compile
它就会在target/scala-version/twirl/
下生成相应的Scala源文件,并且以后缀分组。
比如index.scala.html
会变成html/index.scala
,user.scala.js
会变成js/user.scala
。
Twirl生成的scala文件形如:
package html
import play.twirl.api._
import play.twirl.api.TemplateMagic._
object category extends BaseScalaTemplate[HtmlFormat.Appendable,Format[HtmlFormat.Appendable]](HtmlFormat) with Template1[Category, HtmlFormat.Appendable] {
def apply(category:Category):HtmlFormat.Appendable = {
_display_ {
...
}
}
def render(category:Category,):HtmlFormat.Appendable = apply(category)
def f:((Category) => HtmlFormat.Appendable) = (category) => apply(category)
def ref: this.type = this
}
如果我们想在普通的Scala代码中调用它,如下:
val someCategory = new Category("Scala")
val content = html.category.render(comeCategory).toString()
...
需要注意的是html.category.render(comeCategory)
的返回类型是HtmlFormat.Appendable
,需要调用它的.toString()
方法才能得到一个String
如果我们使用Sbt的命令行来编译,不会遇到任何问题,所有的twirl模板会被正确的编译为scala代码,而所有其它的Scala代码也能正确地找到它们。但是在IDEA中,模板并不会自动的转为Scala代码,所以如果我们直接在其它Scala代码中调用它们,将会报编译错误。
我们需要做两件事:
target/scala-version/twirl
设置为IDEA项目的“源目录”sbt ~compile
,当模板文件发生变化时,自动在后台编译这样IDEA才能及时获取编译后的scala模板文件。需要经常去看看有没有报一些编译错误,及时修正,否则IDEA看到的还是旧代码。稍有不便,但还可以接受。
我们还需要在build.sbt
中添加额外的依赖,否则会报某些类找不到:
libraryDependencies ++= Seq(
"com.typesafe.play" %% "twirl-api" % "1.0.4"
)
这是一套极为强大的Scala模板引擎,静态类型,与Scala相似的语法,功能很多,可以轻松实现出一些复杂的页面(比如通过在模板内定义一个嵌套的函数,生成树):
@showCategory(category:Category) = {
<ol class="posts">
<div class="category_title">@category.name</div>
@for(post <- category.posts) {
<li>
@partials.html.post_title(post)
</li>
}
</ol>
@for(sub <- category.subCategories) {
@showCategory(sub) // 递归在这里!!!
}
}
@showCategory(category)
目前唯一不方便的是,必须手动把它编译成scala文件才能使用,这样的话,我就没办法只提供一些单独的twirl模板直接使用了(而之前用mustachejava)就可以,用户可以随便改,马上用。
我想是有办法让它即时编译的,因为Play2自己就实现了。但是我现在对这个需求不强烈,因为只是自动使用,改完编译一下也不嫌麻烦,以后有需求时再考虑。