Clojure Hiccup的Html渲染和Java Vertx通信的SSR方案

柏舟   新冠4年 01-28

最近使用Clojure+Java把博客的前端重构了,首次加载速度从6秒变为70毫秒,快了85倍。这确实达成了我设计之初的目标:更快的响应速度,SEO支持。但是,我并不推荐使用Clojure方案。

Clojure方案的优势

Clojure方案本质上就是使用领域特定语言DSL,使用Clojure编写Html可以充分组件化,使用Clojure已有的函数。这是Html不可想象的,html中不存在for,if这些逻辑判断,而Django、jinjia、Go Template的DSL虽然可以支持for循环,支持内嵌逻辑,但是很难对html组件化。只能写简单的代码生成器如SQL。

(defn- bread-crumb-li "[{}...] --> [[:li [:a {} xxx]]...]"
  [bread-list]
  (->> bread-list
      ;;  (into [{:content "首页" :href "/"}]) ;; add 首页
       (mapv (fn [{content :content href :href}] ;; to vec
               [:li.breadcrumb-item (if (nil? href) content [:a.lc {:href href} content])]))
                    ;; the last one is active
       ((fn [l]
          (update l
                  (dec (count l)) ;; the last one
                  (fn [tuple] (into [:li.breadcrumb-item.active] (rest tuple))))))))

(defn bread-crumb "input should be [{:content xxx :href xxx|nil}...]"
  [bread-list]
  (if (empty? bread-list) nil
      (let [li-items (bread-crumb-li bread-list)
            ol (into [:ol.breadcrumb] li-items)]
        ;; (log/trace bread-crumb-li "ol: {}" ol)
        (html [:nav {:aria-label "breadcrumb"} ol]))))

上面代码写得稀烂,但是整个编写体验还是非常顺畅的,使用Clojure可以流式地编辑数据。高阶函数和macro,复用代码很方便,而且可以精确的控制预编译的内容。

我使用vert.x作为Java和Clojure的桥梁,Java的http client从后端获取数据,然后通过vert.x的EventBus传给Clojure替换Template,对比从后端请求数据,渲染速度是相当快的。

Java+Clojure的问题

Java+Clojure的编写方案问题还是很多。

首先是语言上的问题,Clojure使用Java的API有点蹩脚。Clojure的生成Java Class存在问题,Clojure本身确实能够生成Java Class,但是我extends Class失败了。编辑器也没有跨语言的支持,插件识别不了clojure生成的.class。Java只能通过vert.x调用Clojure,导致程序的入口需要用Clojure写Java代码,真的很痛苦。

Clojure的数据容器和Java不是完全相同的,很难把vert.x的JsonObject转换成Clojure的map,导致节点间通信只能使用string。

其次,这种动态类型的语言就像没有注解的Python,编辑器没有任何提示,非常容易出错。我感觉我写Go,Java基本不用动脑,但是写Clojure就很累。

这些问题还基本能够克服,最大的问题是Clojure和Clojure的package没有任何的module-info支持,Clojure本身连类都没有,我都不知道怎么export。Java的module本身设计没大问题,但是Java的生态支持太差了,我认为Java的容器部署只适合纯Java编写,跨语言编写存在潜在的问题,我认为Clojure是无法使用jmod和jlink的。

最终使用docker打包时,jar包15MiB,镜像500MiB,而Go、Rust的镜像也就不到50MiB,完全没有可比性。

总结

使用Clojure最大的原因就是编写DSL真的很方便,但是这不代表只有Clojure方案。比如.NET Blazor,可以自己实现一套UI库,编译成html,本质上一样。只是说,Clojure一个人就可以实现DSL,.NET得一个团队做而已。