跟“白屏”说拜拜:用 Next.js 把 React 搬到服务器上,Google 爬虫都要喊一声“真香”

2025博客之星年度评选已开启 10w+人浏览 813人参与

前言:除了你,没人愿意对着空白屏幕发呆

接上回。咱们用虚拟滚动把 10 万条数据治得服服帖帖。现在你的应用在渲染出来之后,性能确实很顶。

但是,有一个更尴尬的问题摆在面前:首屏加载(First Paint)

咱们之前写的 React(CSR - 客户端渲染),工作流程是这样的:

  1. 浏览器请求页面。
  2. 服务器扔回来一个几乎是空的 HTML:<div id="root"></div>
  3. 浏览器加载那个 5MB 的 JS 包。
  4. JS 执行,去调 API,拿到数据。
  5. JS 把 DOM 算出来,塞进 root 里。

这一套下来,用户在前几秒钟看到的都是大白屏
更惨的是 SEO(搜索引擎优化)。Google 的爬虫虽然聪明点了,但百度的爬虫那是相当“直男”。它过来一看:“哟,这网页只有一个 div?内容是空的?垃圾!” 然后转身就走,你的网页在搜索结果里永远排在第 100 页开外。

今天,我们要来聊聊 Next.js服务端渲染 (SSR)。我们要把渲染这脏活累活从用户的浏览器挪到服务器上,让用户打开网页的一瞬间,内容就是满的。

[Image of CSR vs SSR architecture diagram]


观念升级:不仅是框架,是“元框架”

很多兄弟对 Next.js 有误解,觉得它就是个“带路由的 React”。
错!在 2025 年的今天,Next.js 其实是 React 的完全体

React 官方团队现在都明说了:“你要写 React,推荐直接用 Next.js。”
为什么?因为 React 只是个 UI 库(View 层),而 Next.js 帮你搞定了路由、打包、SSR、API 路由、图片优化……它是全家桶。

我们重点要说的,是它的核心大招:React Server Components (RSC)


实战演练:从 useEffect 到 async/await

在传统的 CSR 项目里,我们要获取数据,通常得写个 useEffect,还得处理 loading 状态。

❌ 传统的 CSR 写法(慢、且 SEO 为 0):

// 客户端组件
import { useState, useEffect } from 'react';

export default function UserProfile({ id }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    // 浏览器加载完 JS 后才开始发请求
    fetch(`/api/users/${id}`).then(res => res.json()).then(setUser);
  }, [id]);

  if (!user) return <div>Loading...</div>; // 用户先看 1 秒钟这个

  return <h1>{user.name}</h1>;
}

✅ Next.js (App Router) 写法(秒开、SEO 满分):

在 Next.js 的 App Router 模式下,组件默认就是服务端组件 (Server Component) 。这意味着你可以直接在组件里写 async/await,直接连数据库!

// 这代码是在服务器上跑的!浏览器连一行 JS 都收不到,只收到 HTML。
import db from '@/lib/db';

async function getUser(id) {
  // 直接查库,或者调内网 API,快得飞起
  return await db.user.findUnique({ where: { id } });
}

export default async function UserPage({ params }) {
  const user = await getUser(params.id);

  // 服务器直接把渲染好的 HTML 扔给浏览器
  // 爬虫看到的就是:<h1>Jack</h1>
  return (
    <main>
      <h1>{user.name}</h1>
      <p>{user.bio}</p>
    </main>
  );
}

发生了什么?

  1. 用户请求 /user/1
  2. Next.js 服务器接收请求,执行 getUser,拿到数据。
  3. Next.js 生成完整的 HTML:<main><h1>Jack</h1>...</main>
  4. 浏览器收到 HTML,直接显示内容

没有 Loading 转圈,没有白屏。爬虫一看:“哇,内容好丰富!” 排名蹭蹭往上涨。


进阶技巧:静态生成 (SSG) —— 也就是“作弊”

对于“用户个人主页”这种数据会变的页面,我们用 SSR(每次请求都跑一遍服务器)。 但对于“关于我们”、“博客文章”这种一万年不改一次的页面,如果每次有人访问都去查数据库,那就太冤大头了。

Next.js 有个无敌的功能叫 SSG (Static Site Generation)

你只需要告诉 Next.js:“这页面是静态的。” 它就会在构建打包 (Build Time) 的时候,把这个页面生成为一个 .html 文件。

当用户访问时,Nginx/Vercel 直接把这个 HTML 扔出去。这比 SSR 还要快,因为连数据库都不用查,甚至不需要服务器计算,这就是纯静态资源分发。


// 告诉 Next.js 提前把哪些文章生成好 HTML
export async function generateStaticParams() {
  const posts = await getposts();
  return posts.map((post) => ({
    slug: post.slug,
  }));
}

export default async function BlogPost({ params }) {
  // ... 渲染逻辑
}

避坑指南:Hydration(注水)之痛

虽然 SSR 出来的 HTML 能立马看到,但它一开始是“死”的(没有交互)。 浏览器展示 HTML 后,会加载 JS,把 React 的事件监听器(onClick 等)挂载上去,这个过程叫 Hydration(注水)

如果你在服务端渲染的内容,和客户端注水时的内容不一致,React 就会报错: Text content does not match server-rendered HTML.

常见翻车现场:

  // ❌ 错误!
  // 服务器时间是 UTC,客户端时间是 GMT+8
  // HTML 里是 10:00,JS 算出来是 18:00,直接报错
  return <div>Current time: {new Date().toLocaleTimeString()}</div>;
}

解决办法: 凡是涉及到浏览器特有属性(windowlocalStorage、时区)的,必须放到 useEffect 里,或者用 dynamic import 强制转为客户端组件。

import { useState, useEffect } from 'react';

export default function TimeDisplay() {
  const [time, setTime] = useState('');

  useEffect(() => {
    setTime(new Date().toLocaleTimeString());
  }, []);

  return <div>Current time: {time}</div>;
}

总结:该不该上 Next.js?

如果你的项目是:

  • 后台管理系统(Dashboard) :无所谓,CSR 就够了,没人靠 SEO 搜后台。
  • 企业官网 / 博客 / 电商 / 资讯站必须上 Next.js。不上就是跟钱过不去,跟流量过不去。

SSR 解决了首屏速度,SSG 解决了高并发压力,Server Components 让前端能直接操作后端逻辑。 这就叫降维打击。

好了,我要去把那个还是 create-react-app 的老官网迁移到 Next.js 了。


在这里插入图片描述

下期预告:既然我们都已经能在 Server Components 里直接读数据库了,那是不是连 API 接口都不用写了? 没错!写 axios.post 已经是过去式了。 下一篇,我们来聊聊 “Server Actions:全栈 React 的最后一公里” 。教你如何在按钮的 onClick 里直接调用服务器函数,把“前后端分离”重新“合二为一”。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大布布将军

感 蟹 袋 佬🫡🫡🫡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值