React 2026:为什么全客户端 SPA 正在成为遗留代码

发布: (2026年1月20日 GMT+8 12:03)
7 min read
原文: Dev.to

Source: Dev.to

不再值得的 SPA 权衡

丰富的交互性 是以 JavaScript 膨胀 为代价的。

我们将完整的框架发送到浏览器,以便用户在路由之间点击时无需完整页面重新加载。常见的症状包括:

  • 100–500 KB 的 JavaScript 包 我们的实际代码之前
  • 客户端数据获取的瀑布式请求
  • 令人恐惧的 “加载旋转图标遍布” 体验
  • 水合开销导致交互被阻塞

当时的替代方案是 jQuery 代码乱七八糟或完整的服务器往返,这样的做法还算合理。但在 2026 年,我们有了更好的选择。

混合架构:服务器组件 + 客户端组件

服务器组件:基础

  • 在渲染期间 只在服务器运行一次
  • 可以直接访问数据库、API、文件系统
  • JavaScript 发送到客户端

适用场景: 数据获取、静态内容、布局、非交互式 UI。

// This component NEVER ships to the browser
// It runs safely on your server, fetching fresh data each request
async function ProductDetails({ productId }) {
  const product = await db.products.findUnique({
    where: { id: productId },
  });

  return (
    <>
      <h2>{product.name}</h2>
      <p>{product.description}</p>
      {/* Price is static content – stays on server */}
      {/* AddToCartButton IS interactive – becomes client component */}
    </>
  );
}

客户端组件:交互式岛屿

标记为 use client

  • 处理用户交互、状态、effect
  • 仅在需要的地方发送
  • 适用场景: 表单、按钮、动画、实时更新

关键洞察: 你的大部分应用并非交互式。
你的导航、页脚、主视觉区、产品描述、博客内容——这些不需要 React 状态或 effect。它们只需要渲染。

2026 堆栈:一个具体示例

// 2024 Approach (SPA)
function ProductPage() {
  const [product, setProduct] = useState(null);
  const [reviews, setReviews] = useState([]);

  useEffect(() => {
    // Two client‑side fetches = double latency
    fetchProduct().then(setProduct);
    fetchReviews().then(setReviews);
  }, []);

  if (!product) return null;

  return (
    <>
      {/* price, description */}
      {/* static content */}
      {/* interactive */}
      {/* more fetches… */}
    </>
  );
}
// Bundle: ~150 KB React + 50 KB app code = 200 KB to customer
// 2026 Approach (Hybrid)
// Server Component (runs on server, zero JS to browser)
async function ProductPage({ productId }) {
  const [product, reviews, related] = await Promise.all([
    db.products.findUnique({ where: { id: productId } }),
    db.reviews.findMany({ where: { productId } }),
    getRelatedProducts(productId),
  ]);

  return (
    <>
      {/* Static content rendered to HTML on server */}
      {/* ... */}
      {/* Only these interactive bits become client JS */}
      {/* ~3 KB */}
      {/* ~5 KB */}
    </>
  );
}
// Bundle: ~8 KB of interactive JS only

影响: 用户会立即看到内容。JavaScript 在后台为 “加入购物车” 按钮加载交互功能。没有加载指示器。没有水合阻塞。

为什么这是不可避免的

1. 性能期望已改变

现在拥抱混合未来,您的应用将为2029年及以后做好准备。

Performance Optimization

“不仅要让它快,而且要减少传输,在服务器端计算,最小化水合

安全意识

“了解代码在何处运行,以及在服务器上保管机密”

迁移路径:立即开始

您无需重写应用。遵循 80/20 法则

  1. 识别非交互页面(About、Blog、Documentation)
  2. 将它们转换为 Server Components
  3. 将交互部分提取为 Client Components
  4. 测量包体积的减少情况

预期结果

  • 40‑70 % 的 JavaScript 减少
  • 30‑50 % 的 Largest Contentful Paint (LCP) 加速
  • 更简洁的数据获取代码

反驳(以及它们为何在淡出)

关注点回应
“我们需要 SSR 来做 SEO!”服务器组件会自动提供 SSR,并且对静态部分零客户端 JS。
“我们的用户需要完整的离线支持!”Service Workers + 客户端组件用于关键路径仍然可用。大多数应用并不需要完整的离线。
“这会把我们锁定在某个框架里!”确实如此,但你已经被 React 锁定了。框架只是提供服务器运行时。

展望未来:混合模式之后会是什么?

  • AI‑生成的组件拆分 – 自动优化服务器/客户端边界的工具。
  • 预测性预取 – 能够预测你下一步点击的服务器组件。
  • 边缘组件 – 在 CDN 上运行 React 组件,实现约 10 ms 的全球延迟。
  • WebAssembly 集成 – 通过 WASM 在客户端安全运行重计算,而非 JavaScript。

结论:写在捆绑包上

  • Airbnb 通过 RSC 将 JavaScript 减少了 40 %
  • Vercel 的仪表盘 交互速度提升了 60 %
  • 开发者报告称 数据获取代码更简洁

全客户端 SPA 已经为我们服务了十年。 在技术领域,十年是一段漫长的时间。

未来并不是“更少的 JavaScript”——而是 更智能的 JavaScript:只在需要时、向需要的人发送所需的代码。

你的下一个项目不应再是 SPA,而应是 策略性拆分的应用,兼顾用户带宽和开发者的可维护性。

转型已经在进行。问题不在于 是否 采用混合架构,而在于 你将多优雅地完成这一次转变

你已经在使用服务器组件了吗?
你遇到的最大挑战或收获是什么?在评论区分享吧——我会阅读每一条。

全屏模式演示

<button id="enter">Enter fullscreen mode</button>
<button id="exit">Exit fullscreen mode</button>

<script>
  const enterBtn = document.getElementById('enter');
  const exitBtn = document.getElementById('exit');

  enterBtn.onclick = () => document.documentElement.requestFullscreen();
  exitBtn.onclick = () => document.exitFullscreen();
</script>
Back to Blog

相关文章

阅读更多 »

React Router 中的滚动恢复

介绍 在使用 React Router 构建单页应用程序(SPAs)时,几乎会立刻出现一个常见的用户体验(UX)问题:导航会更改 URL,但 p...