Vue.js、Clojure Hiccup SSR、Asp.Net的Web方案比较
柏舟 新冠4年 06-05
这个博客迭代了三轮了,从最开始的Vue.js到Clojure+Java,到现在的Asp.Net。
Vue.js
由于使用了Element-UI,打包后接近1MB,阿里云有带宽限制,首次加载时间5秒左右,而且Vue.js不方便SEO。虽然可以改成SSR+CSR复合方案,但是我实在是不喜欢Javascript,尤其是各种Webpack,Vite,Esbuild,Babel这些搞不清楚的东西。
但优点也非常明显:
Vue.js状态管理非常好。它可以绑定变量,一旦状态改变就重新渲染。但是仅限于单个组件,父组件只能与子组件单方向通信,否则需要使用emit返回事件或者使用全局存储。我个人觉得emit还算好用,将组件看成一个Actor,封装各种消息进行通信,而且允许容错。
与后端通信只需要json,大大减小了数据量。
只是我的博客只需要渲染页面,不存在状态,Vue.js就是杀鸡用牛刀。
Clojure hiccup+Java
对于博客来说功能上是完备的,而且可以逻辑和页面可以混着写,比如Html就没有办法嵌入for循环,但是Clojure就可以使用map嵌入循环,并且不用像Vue.js那样需要编译,非常简洁。
(defn sitemap [{articles :articlePageView t :tagsAll article-latest :articleLatest} url]
(str
#"<?xml version='1.0' encoding='UTF-8'?>"
(html
[:urlset {:xmlns "http://www.sitemaps.org/schemas/sitemap/0.9"}
(map
#(path-uri url % (get-in article-latest [:cur :datePublished]))
["/" (router-articles-list)])
(map (partial article-uri url) articles) ; map 第二个参数是函数,第三个参数是参数(list)
(map (partial topic-uri url) t)])))
;; [:url [:loc "http://127.0.0.1/topics/2"]
;; [:priority 0.1]]
(defn topic-uri [uri topic]
[:url [:loc (str uri (router-topics (:id topic)))]
[:priority 0.1]])
编译出来的部分结果:
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://127.0.0.1/topics/2</loc>
<priority>0.1</priority>
</url>
<url>
<loc>http://127.0.0.1/topics/3</loc>
<priority>0.1</priority>
</url>
<url>
<loc>http://127.0.0.1/articles/22</loc>
<lastmod>06/01/2023 03:14:44</lastmod>
</url>
<url>
<loc>http://127.0.0.1/articles/21</loc>
<lastmod>05/25/2023 10:13:37</lastmod>
</url>
</urlset>
但缺点是需要同时写Java和Clojure。引入Java的原因是Clojure这种静态语言完全没有类似静态语言结构体的字段提示,导致写起来非常痛苦。设想一下,写一个函数不知道参数的类型是什么,也不知道里面有什么字段。我在开发过程中发现出错了,就只有打log观察数据结构是什么样子,而且作为一个Lisp注定没有办法打断点Debug。
所以在确定性强的部分,我引入Java从后端获取数据,在动态的部分,使用Clojure渲染。数据传递使用了Vert.x,可能的问题就是架构太复杂了。Vert.x是一个事件驱动模型,我使用了其中的Actor模型,每一个获取数据、渲染都单独使用了Actor,事实上完全没有必要。
如果再进行重构的话,能不使用Actor就不使用Actor。因为从后端获取数据本质上就是一个异步操作,注入Vert.x返回一个Future就可以了。Actor更多的是用于状态管理,抽象成Actor反而引入了Actor内部通信失败和序列化反序列化等等复杂度,函数调用根本不会存在这个问题。
这样就从原来的一个View三个Actor变为现在的一个,减少很多通信失败的错误处理。
Asp.Net
Asp.Net在我看来与Clojure+Java方案逻辑是一样的,使用C#获取后端数据,然后在cshtml文件中嵌入C#代码编写Html模板,但是不需要使用Actor进行通信。
由于有巨硬(微软)的支持,调试方面非常好。Clojure+Java每次重载需要重新编译运行,每次10秒左右,但是Asp.Net可以热加载,编译速度非常快,并且可以支持断点Debug。
此外,C#的模板比Python的Django和Go的Template强多了,可以在模板内部嵌入组件,我觉得开发体验和功能跟写Vue的SSR相同(因为Vue的SSR不能有生命周期,那么状态的管理和绑定就没用)。这还是我还没学Blazor,我觉得完全版的Asp.Net跟前端工具链是没有区别的。
C#的模板还有一些独特的地方,它有全局共享的数据结构ViewData,你可以在Layout(写head等通过模板等地方)使用ViewData,在组件内部对ViewData进行赋值。
@* layout.cshtml *@
<head>
<title>@ViewData["Title"]|柏舟的博客</title>
@if (ViewData["Description"] is not null)
{
var desp = ViewData["Description"];
<meta name="description" content="@desp" />
}
</head>
<body>
<main role="main">
@RenderBody()
</main>
</body>
@* article *@
@{
Layout = "_Layout";
ViewData["Title"] = "首页";
}
也就是说,它的渲染顺序不完全是从外到内的,内部的逻辑也会影响外层。这个渲染逻辑顺序还真是挺神奇的。