构建一个目录网站听起来微不足道,直到你发现自己为了展示一个本质上是精选目录的内容,而不得不去搭建数据库、缓存层和响应式前端框架。我最近构建了 Social Tools List,一个用于比较社交媒体软件的网站。我的目标是快速上线、保持高速,并避免为那些仅在我添加或更新工具时才会变化的内容去维护基础设施。我最终选择了一个“静态优先”的技术栈:使用 Astro 进行网站生成,使用 TypeScript 处理结构化数据,并使用 Cloudflare Workers 进行部署。结果是一个加载速度极快、托管成本几乎为零且无需数据库管理的网站。
为什么目录网站采用“静态优先”模式是明智的
许多 Web 应用默认采用服务端渲染或单页架构,因为它们感觉是安全且现代的选择。但并非每个网站在每次请求时都会接收动态的用户输入。Social Tools List 是一个读密集型资源。比较数据是在我推送更新时发生变化的,而不是在访客刷新页面时。提前渲染 HTML 可以消除在边缘侧进行数据库查询、即时模板编译或在浏览器中进行注水 (hydration) 的开销。我在构建时生成网站,部署静态文件,并让一个轻量级的 worker 处理外壳。这保持了极低的响应时间,并消除了整类运行时故障。
将数据存储在 TypeScript 中,而非数据库
我没有使用数据库。目录中的每个工具都定义为一个 TypeScript 对象,包含 slug、名称、域名以及支持的工作流数组。一个典型的条目如下所示:
{
slug: 'buffer',
name: 'Buffer',
domain: 'buffer.com',
workflows: ['scheduling', 'analytics']
}
将数据存储在构建网站的同一种语言中具有两个直接好处。首先,Pull Request 变成了内容审查。当我添加一个工具时,diff 会显示确切的字段和值,队友无需学习 CMS 界面即可发现拼写错误或错误的域名。其次,TypeScript 编译器会强制执行每条记录的结构。如果我忘记包含 slug 或拼写错了工作流键名,构建会在错误数据到达页面之前失败。
使用数据库会引入迁移、连接字符串、缓存策略和备份程序。对于一个由我手动维护、仅有几百个条目的目录来说,这些开销纯粹是累赘。在 TypeScript 模块中使用静态数据是这种模式下最廉价且正确的选择。这里的“正确”至关重要。这不仅仅是为了省钱,更是为了移除那些解决我并不存在的问题的抽象层。
让 Astro 负责路由和渲染
Astro 通过单个动态路由为每个工具生成一个 HTML 页面。我定义一个布局组件,Astro 会自动为每个条目生成元数据、标题和结构化数据。由于相同的数据集驱动着主索引页、工作流分类中心页和单个详情页,因此首页上的卡片绝不会显示与详情页不同的描述。在传统的 CMS 设置中,你经常会看到数据不一致:API 返回一个版本,缓存返回另一个版本,而客户端渲染又返回第三个版本。基于单一事实来源 (single source of truth) 的静态生成防止了这种情况。
Astro 的孤岛架构 (island architecture) 也使得添加小型交互组件变得非常容易,而不会让整个页面被 JavaScript “污染”。网站以静态 HTML 的形式交付,只有过滤脚本会对其特定的 DOM 区域进行注水。整个文档并没有被框架运行时所包裹。Astro 还将页面元数据视为一等公民。每个工具页面都有直接从 TypeScript 记录中获取的 title 标签和 meta 描述,因此我不需要单独的插件或 head 管理库。
无需框架即可实现过滤
搜索和过滤功能经常会诱使开发者去安装 React、Vue 或沉重的状态管理库。我抵制了这种做法。Astro 在服务端将完整列表渲染为纯 HTML。一个不到 1KB 的小型原生 JavaScript 脚本在浏览器中运行,并根据工作流标签或文本匹配来切换列表项的 display 属性。
Serving the complete markup sounds inefficient if you come from an API-driven background. But consider the overhead of a typical dynamic approach. The browser downloads a JavaScript bundle, hydrates a component tree, calls an endpoint, waits for JSON, and then renders rows. For a directory that lists fewer than one hundred tools, that ritual is slower and less reliable than hiding divs that are already in the document. My script attaches event listeners to filter buttons, reads a data-workflow attribute on each row, and sets non-matches to hidden. The operation takes milliseconds.
Because the list is present in the initial HTML, the site is usable without JavaScript. Search engine crawlers see every link and every description. Users on slow networks or with script blockers still get the full directory. The filtering is an enhancement, not a gate.
Sitemaps and Robots as Code
Sitemaps and robots.txt are not afterthoughts written by hand. They are Astro routes that consume the same dataset and URL helpers as
