Play vs. Lift 痛点问题

12

编辑
现在可以通过启用子项目构建来大大缓解编译时间慢的问题,这是一个巨大的胜利。

已经放弃使用Play自带的资源生成器(例如Coffeescript和LESS),转而使用第三方Grunt JS;现在增量构建期间的代码更改仅受scalac编译时间的限制,而不是Play相对较慢的资产生成的开销。

原始内容
总体上对Play 2.1 Scala(2012年9月14日发布,即切换到Scala 2.10之前)还算满意;但是,有一些开发痛点:

1)路由:在路由更改时,整个路由控制器结构可能重新编译:不好。

2)REST似乎不直接支持,因为路由POST /foo/bar/:idDELETE /foo/bar/:id冲突;即路由路径必须是唯一的,可能是为了反向路由。

3)视图:每个foo操作都有一个scala.html文件,文件数量很快增长,这意味着编译时间更慢,需要编译的内容也更多;缺乏泛型支持和盲目编码(当然,据我所知,到目前为止任何Scala模板引擎都没有IDE支持)是特别棘手的问题。

4)增量构建可以工作,但是过程中没有任何可以称为“敏捷”的东西,即使对scala.html文件进行简单更改实际上也需要大约2秒钟的时间,在希望获得即时代码更改浏览器刷新反馈周期的情况下,这是一个很长的时间。

我知道Play开发人员正在解决上述问题,而慢速构建时间也直接与sbt、Scala版本和自己的代码结构有关。总的来说,Play是一种令人愉快的开发体验。然而,这是关于痛苦的问题,我想知道Lift在这方面提供了什么......

Lift似乎采用了不同的方法。Lift用户是否遭受上述问题?假设没有,因为MVC不是Lift,而xml样式片段方法可能不会产生与Play的幕后构建机制相同的编译时间影响。

Lift中的痛点是什么?


1
我们团队似乎不存在这个问题。我们使用POST来创建还没有ID的内容,使用PUT来更新已有ID的内容。所以,在/foo/bar/:id上使用PUT和DELETE是有效的,而POST应该在/for/bar上使用。 - Nick
2个回答

24

作为一个使用Lift约两年的人,以下是我的个人看法:

1) 路由:在路由更改时,整个路由控制器结构都需要重新编译:不好

Lift中没有路由。我认为最接近的相关概念应该是SiteMap,就个人而言,我从来没有遇到过与其编译有关的问题。

2) REST似乎不被直接支持,因为路由POST /foo/bar/:id会与DELETE /foo/bar/:id冲突;即路由路径必须唯一,可能是为了反向路由。

通过使用Lift,我已经做了很多REST,并且可以告诉你这绝对不是问题。Lift的REST支持非常棒, 并且基于Scala的模式匹配,这为您设计Web服务提供了一个非常强大、类型安全的方式。

"3) views: 每个foo操作都有一个scala.html文件,文件数量增加很快,这意味着编译时间变慢,需要编译的内容也更多;由于缺乏IDE支持(当然,据我所知,目前没有任何Scala模板引擎提供IDE支持),泛型不受支持,盲目编码尤其困难。"
"Lift中,HTML代码仅仅是HTML(没有特殊符号),因此根本不会影响编译时间。HTML(称为模板)通过Snippet进行处理,将NodeSeq => NodeSeq转换。这听起来很复杂,但是Lift有一个DSL让它变得非常容易。想要将用户姓名添加到span标签中吗?如果它看起来像下面这样: "
<span id="user-name">User name goes here</span>

在您的代码片段中可能会有这样的代码:
"#user-name *" #> user.name

你可以在模板中重复使用项目,比如表格或列表:


<table id="table"><tr><td class="name"></td><td class="value"></td></tr></table>

应用此后:
val tuples = List(("Lift", "Is great"), ("Other web frameworks", "Eh"))
"#table" #> {
  "tr" #> {
     tuples map { case(name, value) =>
       ".name" #> name &
       ".value" #> value
     }
  }
}

会生成一个表格,其中有两行,每一行代表列表中元素的名称/值。
我认为这真的是Lift最大的优势之一。模板只是HTML,没有符号或标记。你可以直接使用设计师提供的内容,并且甚至可以让他们直接进行更新(在某些情况下)。
另一方面,片段是纯Scala,而不是某种模板语言。您可以在Snippet中使用Scala的所有功能,并且它都经过编译器检查。
还可以(并且鼓励)在多个页面上使用Snippet,因此您不一定需要每个页面都有一个Snippet。您甚至可以配置Sitemap以在多个页面上使用相同的模板,并根据请求向页面包含的Snippets传递类型安全参数。
“4)增量构建可以工作,但是该过程中没有任何东西可以称之为“快速”,即使对scala.html文件进行简单更改实际上也需要@2秒的时间,这在您想要即时代码更改浏览器刷新反馈循环时是很长的时间。”

我认为Lift在这方面并没有什么问题,但不幸的是它也没有太多帮助。很高兴听到Scala 2.10将在这个领域包含一些改进,因为我认为它们必须来自编译器。

回答一些Lift的批评...

需要高级Scala吗? 不,我不认为需要。 这有点主观,但您可以从我发布的内容中看到,创建模板并将片段应用于它相当简单。 您必须熟悉像“map”这样的概念,但如果您不熟悉,使用Scala Web框架有什么好处呢? 对于新手来说,他们可能更好地遵循Wiki CookbookSimply Lift中的示例,而不是API文档,但我认为这是Scala习惯用语,而不是Lift用语。

你是否必须在“控制器”中混合标记?绝对不是。我将忽略Lift不是MVC框架的事实,并假设发布者谈论的是Snippets。从Snippet输出HTML绝不是必要的,在大多数情况下是完全反模式。像我发布的那样的CSS选择器允许您将所有HTML保存在模板文件中,将所有逻辑保存在Snippet中。
Lift需要太多状态吗?这是我遇到的头号投诉,但我从未见过它伴随着真正的世界问题。事实是,使用Lift时,您可以选择是否要有状态。如果您正在使用Lift编写Web服务,并且在访问URL时不希望创建会话,则可以使用LiftRules.statelessDispatchTable为该服务注册。这符合Lift哲学,即状态既不好也不坏,但它对于满足某些需求是必要的,而对于其他需求则不是必要的。重要的是明确何时使用它并让开发人员决定。如果您想了解更多详细信息,请参考David Pollack的better explanation

2
+1,全面深入,但不痛苦!我不相信,Lift不可能那么好;-) "#table #> {..."这一部分乍一看相当丑陋。在视图中,我实际上对HTML标记没有任何问题,实际上更喜欢它,因为这是我在Firebug中查看的内容。无论如何,听起来至少Lift由于较少的移动部件/需要编译的内容提供了更快的体验。好奇心驱使,当我从当前的Play 2.1项目中喘息过来时(正在生产中撕裂,天哪,JVM速度惊人),我会尝试使用Lift。 - virtualeyes
11
我想你询问了Lift的痛点,我可能有些专注于你特定的问题并反驳其他回答 :) 无论如何,我认为与Play相比,Lift有两个主要的痛点:(1)您可以在更改时获得相当即时的反馈,但确实需要更多的配置(JRebel),以及(2)文档组织得不太好。 Wiki、Cookbook等都是很好的资源,但将所有信息放在一个地方会更好。不过,Google Group是最棒的,我认为它弥补了文档问题。 - Dave Whittaker
@DaveWhittaker 当我说Scala复杂性时,我指的是源代码(会让它更清晰),我非常依赖框架源代码来理解发生了什么。Lift是其中之一,当我尝试(两年前)时,对于Scala初学者来说是噩梦 :) - Pere Villega
1
你的例子中第二个表格单元格的类名不应该使用 value 吗? - Augustus Kling
只是一个小提示。 从Lift-2.5开始,表达式:““#table” #> { “tr” #> {...}” 可以简化为:““#table tr” #> tuples.map{...}”。而且,一般来说,所有的转换选择器都非常像CSS选择器,这非常方便。 - VasiliNovikov

8

首先,我认为有必要回应你提出的一些观点:

  • 关于1和4:Scala 2.10在编译速度方面有了显著的提升,这不再是一个问题
  • 关于2:我从未看到过GET/POST冲突的情况,但我没有尝试过DELETE。这可能是Play或您的代码中的错误(请打开一个新问题,包括完整的路由定义,包括方法)
  • 关于3:IntelliJ 12支持Play 2,并且泛型在模板中也得到了支持(如果您所说的泛型是指传递类型为X [A]或A的通用参数)

我对lift的经验是几年前的事情了,因此其中一些观点可能已经不适用:

  • Lift的代码库和语法(源代码)非常高级,这使得刚开始接触Scala的人很难理解框架代码的含义。
  • 视图优先的方法混乱不堪,在“控制器”中混合代码(返回HTML),破坏了关注点分离,使得代码更加难以跟踪。

但最重要的是:

  • 有状态。自从我离开Java EE世界并开始使用无状态服务器以来,问题的数量已经惊人地减少了。不必担心会话和其他杂乱无章的事情似乎不重要,但确实会有所区别。特别是在云计算时代 :)

2
+1 很好,感谢你的解释。Scala 2.10 + sbt 13 应该会有所帮助,希望不久就能提供稳定版本。关于 REST 路由,是的,DELETE 可能是个边缘案例,不太确定。我不知道 IntelliJ 的这一点,在 Linux 上,最后我检查了一下 IDE 看起来像是狗吃的早餐,对眼睛很苛刻。通过泛型,我的意思是 "@[T <: Foo](bar: T)";如果没有完整的泛型,你不得不创建更多的类型和 scala.html 文件来处理各种情况,这意味着更长的构建时间。 - virtualeyes
@virtualeyes 像那样的内联方法可能无法正常工作,但请尝试将它们创建为标签。它们应该可以解决这个问题。 - Pere Villega
1
我对状态性有相反的经验:在Lift中,它绝对是类型安全的,并且它非常有帮助。我从来没有遇到过任何问题(使用Lift已经1年了,现在使用Scala已经1.5年了)。 - VasiliNovikov
3
“视图优先”方法很混乱,将代码混合在“控制器”中(返回HTML)-是的,我也曾经在控制器中返回HTML几个月,直到我发现没有那样做会更加舒适。之后,我的控制器中就没有HTML了,效果很好。 “视图优先”方法成为了我非常喜欢的东西。 - VasiliNovikov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接