文章目录
- 一、什么是 Next.js 的同构?
- 二、核心目录结构
- 三、关键函数:如何实现不同渲染方式?
- 1. getServerSideProps —— 实现 SSR(每次请求动态获取数据)
- 2. getStaticProps + getStaticPaths —— 实现 SSG(构建时生成)
- 四、Hydration:服务端渲染 + 客户端交互
- 五、客户端/服务端代码混用的注意点
- 六、完整例子:同构页面流程图
- 七、总结
在如下文章我们介绍了同构和三大渲染场景:
- WHAT - 前端同构 Isomorphic Javascript
- WHAT - SSR vs SSG vs ISR
接下来我们主要去深入了解 React 的同构实现,Next.js 是最好的起点。它为你封装好了复杂的同构逻辑,让你专注于构建组件和页面。
下面我会帮你系统性地理解 Next.js 的同构机制,包括目录结构、渲染方式(SSR/SSG/ISR)、客户端与服务端代码共存的原理。
一、什么是 Next.js 的同构?
在 Next.js 中,“同构”意味着:
- 你的 React 页面可以在服务端渲染出 HTML。
- 然后发送给浏览器,并在浏览器上"水合"成可交互的 React 应用。
一套代码,同时运行在服务器和浏览器,页面内容和结构一致。
二、核心目录结构
my-next-app/
├── pages/ # 路由页面(同构重点)
│ ├── index.tsx # 首页(自动匹配 / 路由)
│ ├── about.tsx # /about 页面
│ └── [id].tsx # 动态路由页面 /xxx
├── public/ # 静态资源
├── components/ # 可复用的 React 组件
├── styles/ # 样式文件
└── next.config.js # 配置文件
每个 pages/xxx.tsx
页面都会被 Next.js 编译为同构页面 —— 即同时具有 SSR/CSR 能力。
三、关键函数:如何实现不同渲染方式?
1. getServerSideProps —— 实现 SSR(每次请求动态获取数据)
// pages/post/[id].tsx
export async function getServerSideProps(context) {const res = await fetch(`https://api.example.com/post/${context.params.id}`);const data = await res.json();return { props: { data } };
}
- 每次请求服务器都会运行这段逻辑,返回 HTML。
2. getStaticProps + getStaticPaths —— 实现 SSG(构建时生成)
// pages/post/[id].tsx
export async function getStaticPaths() {const res = await fetch('https://api.example.com/posts');const posts = await res.json();return {paths: posts.map(post => ({ params: { id: post.id.toString() } })),fallback: false, // or 'blocking' for ISR};
}export async function getStaticProps({ params }) {const res = await fetch(`https://api.example.com/post/${params.id}`);const data = await res.json();return { props: { data }, revalidate: 60 }; // ISR:60秒更新一次
}
- 初始构建时预生成页面,后续请求直接返回静态 HTML。
- 使用
revalidate
支持增量静态更新(ISR)。
四、Hydration:服务端渲染 + 客户端交互
-
服务端阶段:
- Next.js 在 Node.js 环境执行页面组件。
- 使用
react-dom/server
渲染为 HTML。 - 将 HTML 和页面初始数据一同发送给浏览器。
-
客户端阶段:
- React 在浏览器中调用
hydrate
方法,把 HTML “接管”。 - 绑定事件、状态等功能,实现交互。
- React 在浏览器中调用
这就是同构的本质:HTML 是 SSR 渲染的,交互是 CSR 接管的。
五、客户端/服务端代码混用的注意点
在 Next.js 中你可以写出这样的代码:
import { useEffect } from 'react';export default function Page() {useEffect(() => {console.log('只在浏览器中执行');}, []);return <div>Hello World</div>;
}
✅ 这段代码在 SSR 阶段会忽略 useEffect
,只渲染 HTML
✅ 到浏览器中后,useEffect
会执行,实现动态行为。
如果你需要只在服务端运行逻辑,可以用:
if (typeof window === 'undefined') {// 服务端环境
}
六、完整例子:同构页面流程图
User 请求页面 ──> 服务器运行 React 页面(SSR) ──> HTML 返回给浏览器│带上初始数据(props)↓浏览器加载 React、hydrate 成交互页面
七、总结
项目 | 是否同构 | 渲染方式 | SEO 支持 | 首屏速度 |
---|---|---|---|---|
React CRA | ❌ | CSR | ❌ | 慢 |
Next.js SSR | ✅ | 服务端渲染 | ✅ | 快 |
Next.js SSG | ✅ | 静态生成 | ✅ | 非常快 |
Next.js ISR | ✅ | 静态 + 动态混合 | ✅ | 快且智能 |