博客系统的又一次大重构

2018/07/16

最近在网上瞎逛,发现了一个使用 F# 编写的服务端框架:Giraffe。它除了拥有函数式编程的优雅之外,在自带了一个 Giraffe View Engine —— 可以使用类似 Elm 的 API 来创建服务端页面模板,这对我这种苦于 Razor 很久的人来说简直是充满了重构的诱惑。另一个使我更加坚定着手重构的原因就是 Giraffe 可以非常容易的与 ASP.NET Core 接入,只需 3 行代码,就可以让一个 Giraffe APP 成为 ASP.NET Core 中间件。

Razor 的大包袱

Razor 是 C# 与 HTML 的杂交语言,专门用来编写服务端模板,Razor 的历史也由来已久,在 ASP.NET 平台上,编写 Razor 模板几乎是人人必备的技能。不过越是拥有这种属性的事物就越是拥有难以摆脱的历史包袱,对于 Razor 来说,这个包袱就是代码复用。原先有 Partial View 现在有 tag helperViewComponent ,这些功能的目标都是为了能够把视图部分拆分成更小的模块以便复用。然而,这么多年过去了,Razor 还是不能很方便的做到标签级的组件复用,除此之外,视图组件还无法接受其他视图组件作为参数。不过我们也能从 Razor 最近的改动上可以看到它其实一直在向更细粒度的模板功能努力,要不然怎么会有 Tag Helper 这样的东西。

Giraffe 的进击

不过与其等着 Razor 更新,不如直接换掉 Razor 😏。所以我选择使用 Giraffe 作为 Razor 的替代品,Giraffe 自带了一个 Giraffe View Engine,这是一个非常函数式的模板引擎,它类似 Elm 的 DSL 语法给了我极大的想象空间。带着内心抑制不住的喜悦,我仔细地阅读了官方文档,Giraffe 带给我的是一种相见恨晚的感动:任何一个返回值类型为 XmlNode 的函数都可以作为模板使用,在 Razor 中各种各样的概念与构建块统统不见,留给代码编写者的只有最纯粹的 Function

Razor 中各种 @section 完全不需要,我们需要的只是一个接受 XmlNode list 作为参数的纯函数。Razor 中的 Tag HelperView Component 在 Giraffe 的面前也变成了及其冗余的存在,一个返回 XmlNode 或者 XmlNode list 的函数就已经足够了。

将 Razor 代码迁移到 Giraffe 是一个比较美妙的过程,看着一行一行由纯函数编写成的 DSL 逐渐构建出网页视图的本来样貌,这种体验无异于在电视上观看生物进化带来的奇妙感觉。

使用 Giraffe 时遇见的小阻碍

  1. FSharp 拥有不同于 .Net Framework 的部分常用类型,如 List,虽然类型名字与 System.Collection.Generic.List 一样,但是所处名称空间不同,并不是同一种类型,但是可以相互转换。
  2. FSharp 的拓展方法与 C# 的拓展方法使用了不同的实现,导致 F# 的拓展方法无法在 C# 中调用,需要使用 [<System.Runtime.CompilerServices.Extension>] 对拓展方法所处的静态类与静态函数进行装饰。