科技前沿 科普Web 基础教程建站系列

为什么需要框架?从白物集看 Astro 的选择

📅 2026-07-10

上周我们用纯 HTML + CSS 做了一个个人主页,代码整洁、页面美观。但你有没有想过——如果网站有几十个页面、需要从数据库读取数据、还要处理用户登录和搜索,纯手写 HTML 还能撑住吗?

答案是撑不住。这就是框架存在的理由。

纯手写的三条死线

先看三个具体问题,每个都是真实项目中踩过的坑。

问题一:重复代码爆炸。 每个页面都有导航栏、页脚、<head> 标签。纯 HTML 的做法是复制粘贴——导航栏改一个链接,所有页面都得手动改一遍。10个页面勉强能管,100个页面直接崩。

问题二:数据怎么塞进 HTML? 个人主页的内容是写死的。但一个内容网站的文章、标题、摘要都来自数据库。纯 HTML 没法在页面里写 fetch 然后把数据填进 <div>——你只能在浏览器端用 JavaScript 做,意味着白屏、闪烁、SEO 等于零。

问题三:构建和部署靠手动。 没有压缩、没有代码分割、没有热更新。改一行 CSS 要手动刷新浏览器。

白物集在第一阶段也经历过这些问题。早期原型用静态 HTML 配合客户端 JS 渲染,遇到的就是这三条死线。后来迁移到了 Astro。

Astro 做了什么

Astro 是一个静态站点生成器(SSG)+ 服务端渲染(SSR)框架。它不是「又一个 React」,而是换了一条思路。

白物集的首页 src/pages/index.astro 是典型的 Astro 页面:

---
import BaseLayout from '../layouts/BaseLayout.astro';
import { getArticles } from '../lib/api.js';
import { AI_CATEGORIES, APPLE_CATEGORIES, TECH_CATEGORIES } from '../constants';

const { articles: all } = await getArticles({ limit: 100 });

const aiArticles = published
  .filter((a) => AI_CATEGORIES.includes(a.category))
  .sort((a, b) => new Date(b.published_at) - new Date(a.published_at))
  .slice(0, 3);
---

<BaseLayout title="首页">
  <section>
    <!-- 模板里直接写 HTML,数据已经是渲染好的 -->
    {aiArticles.map(a => (
      <a href={`/articles/${a.id}`}>{a.title}</a>
    ))}
  </section>
</BaseLayout>

注意上面 --- 之间的部分。这段代码在服务器上运行,从 API 获取数据,把结果填入模板,然后返回完整的 HTML。浏览器收到的不再是一个空壳加一堆 JS 脚本——它直接看到了文章标题。

这是 Astro 最核心的设计:零 JS 默认输出。页面模板中的 {#each}{if} 在构建时或请求时就已经计算完毕,不会把循环逻辑打包成 JS 发给浏览器。

组件化:解决重复代码

Astro 的组件系统解决了导航栏和页脚重复的问题。

白物集的 BaseLayout.astro 定义了所有页面的公共结构:

---
import NavBar from '../components/NavBar.astro';
---
<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>{title} · 白物集</title>
  </head>
  <body>
    <NavBar />
    <main class="wrapper">
      <slot />
    </main>
    <footer>...</footer>
  </body>
</html>

每个页面只需要写内容区的代码,<slot /> 就是 Astro 的插槽机制——页面内容自动填入这个位置。导航栏、页脚、SEO 标签全部由布局组件统一管理。

改导航栏链接?只改 NavBar.astro 一个文件,所有页面自动生效。

SSR API 客户端:白物集的双层缓存

说到数据,白物集没有在前端直接调 Supabase——中间隔了一层 Express API + Astro SSR 缓存层。

看一下 src/lib/api.js 的核心逻辑:

const cache = new Map();
const TTL = {
  list: 60_000,
  detail: 120_000,
};

function cached(key, ttl, fetcher) {
  const now = Date.now();
  const entry = cache.get(key);
  if (entry && (now - entry.time) < ttl) {
    return entry.data;
  }
  return fetcher().then(data => {
    cache.set(key, { data, time: now });
    return data;
  });
}

export async function getArticles(opts = {}) {
  const p = new URLSearchParams({ limit: String(opts.limit || 50) });
  const cacheKey = '/articles?' + p.toString();
  return cached(cacheKey, TTL.list, () => fetch('http://127.0.0.1:3001/api/v1' + cacheKey));
}

这个设计回答了「框架能做什么」这个问题:框架让你有能力在服务器端加一层缓存。

纯静态 HTML 做不到——每篇文章一更新,你得重新生成所有 HTML。纯客户端 JS 也做不到——每个用户打开页面都要发一次 API 请求。

Astro SSR 配合进程内缓存:60 秒内相同请求直接走内存,下一页访问零延迟。

Astro 的 SSR 模式:一次迁移带来的改变

白物集最初是纯静态站点(SSG),每次发文章要重新构建、部署、重启。后来切换到 @astrojs/node 的 SSR 模式。

配置 astro.config.mjs 里就几行:

import { defineConfig } from 'astro/config';
import node from '@astrojs/node';

export default defineConfig({
  output: 'server',
  adapter: node({ mode: 'standalone' }),
});

output: 'server' 告诉 Astro:别在构建时生成所有 HTML 了,在服务器上运行时按需渲染。文章发布后,API 里有了新数据,下一个请求就已经能看到,不需要重新构建。

但 SSR 的陷阱也随之而来。白物集踩过一个典型坑:首页按 b.id - a.id(数据库自增 ID)排序,旧文章 ID=100~144 远大于新 API 文章 ID=40,新文章被 .slice(0, 3) 切掉,在首页消失了。排查发现是排序键没用 published_at。修复后改成了:

.sort((a, b) => new Date(b.published_at) - new Date(a.published_at))

这种问题在纯静态 HTML 时不会出现——因为所有页面都是手动维护的。但用了框架就要理解它的数据流。框架帮你解决了重复劳动,代价是你必须搞清楚数据从哪里来、经过什么变换、最终怎么出现在页面上。

那为什么不直接用 React/Vue?

很多人觉得「学框架 = 学 React 或 Vue」。Astro 的不同在于:它不强制你用任何 UI 框架。

白物集的导航栏、文章列表、频道页全部是纯 .astro 文件——没有 JSX,没有 useState,没有虚拟 DOM。Astro 组件就是 HTML 模板加服务器端逻辑。只有在需要客户端交互的地方(比如主题切换按钮),才写一个轻量的 <script> 标签。

这意味着:你可以先学 Astro 的组件语法(和 HTML 几乎一样),而不是一上来就被 hooks、响应式、状态管理搞得头晕。

总结

框架不是「给高手用的工具」——恰恰相反,框架是为了让你不需要重复造轮子、不需要手动拼接每一块砖。

Astro 的选择对白物集意味着: - 导航栏、页脚写一次,全局复用(组件化) - 文章数据从 API 读取,服务器端渲染成 HTML,用户打开直接看到内容(SSR) - 60 秒内相同请求零延迟(进程缓存) - 发布文章不需要重新构建(SSR 模式)

下一篇我们打开编辑器,用 Astro 的组件语法写第一个页面——从 LayoutNavBar 到手把手搭一个频道页,你会看到框架到底省了多少代码。

← Chrome DevTools MCP:AI 代理调试利器 → Apple 频道 Astro 入门:组件化开发你的第一个页面 →
🍎 Apple 深度分析
本文基于 Apple 公开资料及行业分析撰写。观点仅供参考与学习交流。