新年的最佳实践
2022 年 01 月 01 日
赶上封城,在家隔离了好多天。工作的项目没有任何 Bug 出现,实在是无聊。因此这段空闲时间,我终于下定决心把网站重构了一版。重构时试验了一些新的工具和框架,下面是一些值得总结的最佳实践。
静态网站生成器(SSG):Zola
说起来,我已经试验过好多种 SSG 了。不夸张地说,Jamstack 上面的 SSG,我至少试验过 8 种:Next.js / Hugo / Gatsby / Jekyll / Nuxt / Hexo / GitBook / Gridsome。说实话,各有各的好处,但也各有各的缺点。由于我最近特别喜爱使用 rust,因此这次换用了基于 rust 实现的 zola。其实各种 SSG 都有对应的使用场景,只能说需求决定选择。这里我简单列举一下我感受到的 zola 的特点,当然不会只是优点。
轻快易用
zola 很轻,只有一个可执行文件;同时执行速度也很快;在这一点上,只有 hugo 与其类似。其他 SSG 就没这么轻快了。不管是依赖于 nodejs,还是 ruby,都会导致各种环境依赖,以及速度的下降。
关于易用性,无论是命令行的使用,还是使用 rust/cargo 自行修改编译,都很方便。
清爽的目录结构
zola 目录布局特别简单。必须的目录只有 3 个:
- 内容放在
content
- 资源放在
static
- 模板放在
template
另外非必须的目录也只有两个
- 顾名思义的
sass
- 放置第三方主题的
themes
基本上哪些东西放在哪里都很清晰。顺便说一句,第三方主题也是一个 zola 工程,目录结构是相同的。因此查找模板和资源时,优先寻找当前目录,再查找主题目录,这个逻辑非常符合直觉。作为对比,我看 hugo 的目录结构时就有点头昏眼花,不知道东西应该放到哪边。
配置灵活性尚可
从源码角度来看,配置散落在 /config.toml
以及内容目录中 md 文件的 front matter 中。编译时会将这些配置放在上下文中的 config
/section
/page
三个对象中。各对象中都有一些预定义的可配置项,可以简单的从官网文档中查询;如果需要扩展可配置项,可以放在对象的成员对象 extra
里面。
从使用角度来看,编译时页面所需数据基本都可以通过上面的几个对象获取到。也可以通过 __tera_context
对象查看。即使无法直接获取,也有一些辅助函数可用。基本可以满足需求。
我遇到的一个明显的缺点是,如果需要从几个对象中,按照自定义的优先级查询,那么就没有现成的方式了,只能自行实现函数来获取。
代码逻辑自由度一般
本质上来讲,SSG 就是 模板 + 内容 + 配置 + 代码逻辑 = 静态网页。代码逻辑主要靠模板语言来完成。zola 使用的模板语言是 tera。基本能满足需求,但我觉得表现力还是有些限制。比如,编译期一些变量不能通过 macro 来生成和传递到外部;include 无法传参等等。虽然也不是没有办法解决,但可能会因为这些限制,导致一些违反 DRY 原则的写法出现。
主题较少
我觉得这是 zola 没有太多人使用的主要原因。不管是 hugo、jekyll 还是 gatsby,可选的主题都比 zola 多不少。对于像我一样不擅长做页面的人来说,这的确是个问题。不过,一切皆有代价。选择一个现成的主题,直接的后果有两个:
- 主题本身的限制。
大部分主题都是针对 blog 实现的。可以保证你生成一个漂亮的 blog,但仅此而已。如果你需要增加一些自己的页面,那么你需要先看懂主题的实现方式,再根据其风格扩展。这部分实现起来并不简单。 - 选择一个主题,意味着也被对应的工具绑定了。
举例来说,我之前使用 hexo 的 Next 主题。功能很全面,但由于我不知道那些功能是怎么实现的,因此不容易从 hexo 切换到其他 SSG 工具。即使是简单的 hexo 或 Next 升版,也会让人担心是否会因为兼容性问题导致页面渲染错误。
对于自行实现主题的难点,我这次找到了 tailwind 来解决,接下来会解释。
页面框架:Tailwind
之前也用过好几个框架,比如说 bootstrap,或者 vuetify 等等。绝大部分框架都是类似的。先告诉你如何布局,给你一系列辅助的 class,再配合大量控件(当然,部分复杂的高级控件要收钱),还有一些简单的 javascript 来控制控件行为。
Tailwind 完全是反其道而行之。它不给你控件,只给你 class,而且是超多的 class。这就是所谓原子化 css。
很多人在评价 tailwind 时,总是拿关注点分离出来说话,仿佛不写 css,不把组件的样式 wrap 起来,就不是最佳实践了。但实际上,tailwind 首页上,作者已经写了一篇文章来探讨这个观点所存在的问题。
使用感受
我不想谈所谓的理论,我只想记录一下使用 tailwind 之前和之后的感受。
在使用 tailwind 之前,我的设计流程通常是这样的:
- 确定要做的组件
- 为这个组件实现一个或几个对应的 class
- 编辑这些 class 中的样式
- 在网页中调试
说起来很清晰,但做起来实际并不方便。组件不一定可以单独调试,class 不一定只放在一处,还有预处理的问题。简单的样式好说,样式稍微复杂一些的时候,光在编辑器和浏览器之间来回切换查看这件事,就让人很不舒服。至于组件放在布局中带来的其他问题就更不用说了,各种莫名其妙的小错误需要在 devtools 里面仔细查看导致问题的 css 条目,调试起来真的让人无所适从。
使用 tailwind 之后,一切都不一样了。流程变成这样:
- 在页面中准备好 div 嵌套。只要想清楚大概的页面造型就可以做了;即使写错,改起来也不复杂。
- 在编辑器中编辑 class,同时在浏览器中查看页面变化(使用动态重加载,无需手动 refresh)。
- 想不起来对应的 class 时,查询官方文档。
不需要切换文件进行编辑,关注点只在需要调试的 html 上,这样明显提高了效率。同时,由于 tailwind 通过 preflight 过程清除掉了所有预设样式,后续添加样式时如同在一张白纸上作画,不需要考虑可能的样式冲突,写起来畅快肆意。
遇到的问题和解决方案
Tailwind 的原理是,扫描所有 html 中使用的 class,根据 tailwind 的配置,输出一个 css 文件。
zola 目前无法直接跟 tailwind 整合,但可以使用 tailwind CLI 的 watch 功能,持续输出到 static 目录,zola 就可以直接使用生成的 css 文件。也就是说,当修改 html 时,tailwind CLI 发现这个改动后,会先输出一个 css 文件,然后 zola 发现 css 改变后,会再更新一次生成的 html。
默认的 tailwind CLI 是 nodejs 版本,在 zola 这样的非 nodejs 工程中,无法使用 plugin。而 @tailwindcss/typography 是渲染 markdown 所必须的插件。不过,就在前几天,官网提供了 Standalone CLI,解决了这个问题。
官网没有提供控件,只是提供了一个独立的控件网站,是收费服务。但照着上面的截图,很简单就可以做出类似的效果。其他网站上可参考的控件也有很多,毕竟只是一段 html 代码,拿来之后修改也很轻松。没有所谓的标准控件,这正意味着自由。
另外,自行实现控件,免不了涉及动态效果问题,这时可以参考 alpinejs,跟 tailwind 简直是绝配。在 Alpine Toolbox 上可以找到大量带动态效果的控件供参考。
命令解释器:just
每个工程都会使用各种命令进行各种操作,这种工作通常都是用构建工具完成,比如 make/ant/maven/gradle 等等。简单的情况下,直接写点 shell script 也能解决问题。不过现在有一个更好用的工具,就是 just。
使用方式就不多说了,相对于各种构建工具,just 的好处就是易于修改;相对于 shell script,just 又更强大。因此,对于 SSG 工程,just 正如其名,是一个刚刚好的工具。