看了就会的Next.js SSR/SSG 实战教程
Next.js是基于React的服务端渲染工具。在传统的React项目中,例如使用Create-React-App创建的项目,最终build生成的静态文件,是基于浏览器渲染的,即所谓的CSR(Client-side Rendering)。CSR往往都是单页面应用,即一个HTML文件和若干个js、css文件。打开build后的HTML文件,发现代码很简单,页面和组件的元素都是放在了js里,由js动态渲染到HTML中。CSR模式是目前前端开发项目中应用最为广泛的。但有些也场景,特别是需要SEO优化的时候,CSR就不太合适了,所以服务端(Server-side Rendering)渲染应运而生,SSR是由服务器将用户请求的页面DOM组装好后,再返回给浏览器,因此通过“查看网页源代码”,是可以看到完整的页面DOM的。而SSG(Static Site Generation),顾名思义就是静态网站生成,也就是常说的“网页静态化”,除了适合SEO,还很方便CDN加速,比较适合内容相对比较固定的资讯发布类网站。
以下是关于CSR、SSR、SSG的简单对比:
CSR | SSR | SSG | |
运行端 | 浏览器 | 服务器 | 服务器 |
静态文件 | 单页面 | 由服务器即时生成 | 多个页面 |
SEO | 不适合 | 适合 | 适合 |
静态文件CDN | 适合 | 不适合 | 适合 |
适用场景 | 中后台产品 | 信息展示型网站 | 内容较为固定的资讯类网站 |
本教程基于Next.js技术框架,通过一个简单的实战项目,将工程搭建、开发、部署、自动化等环节完整讲述。如果你正在做类似CMS的项目,并且需要将网页内容静态化。那本教程将非常适合你。SSR与SSG在开发过程中几乎是完全一样的,只是最终部署的环境有所不同。掌握了SSG,那SSR也同样不在话下。
以我的个人经验来讲,从零基础学习一个新的框架,最佳的方法是结合项目边做边学,官方技术文档比较适合做手册来查阅。脱离了实战项目,只看官方技术手册,很难掌握。因此,本教程就是以实战项目的角度,把官网技术文档的主要内容串起来。相信按照本教程操作一遍之后,就能快速掌握Next.js。再回过头去系统地看一遍官方技术手册,那就会更加深入理解。
先睹为快
先看下目录了解本教程都有哪些内容。
1 创建Next.js项目
• 1.1 安装Next.js
• 1.2 设置项目目录
• 1.3 项目入口文件
• 1.4 精简项目
2 配置项目
• 2.1 设置路径别名
• 2.2 配置SourceMap(不建议设置)
• 2.3 设置页面title
• 2.4 设置HTML框架代码
• 2.5 以SSR模式运行项目
• 2.6 设置404/500页面
3 CSS预处理及使用
• 3.1 集成Sass/Scss
• 3.2 集成Less(选读)
• 3.3 集成Stylus(选读)
• 3.4 关于样式命名规范
• 3.5 配置全局样式
• 3.6 配置页面(pages)样式
4 页面路由
• 4.1 优化index页面和样式文件的存放位置
• 4.1.1 方法一:通过next.config.js配置
• 4.1.2 方法二:通过组件引入(推荐)
• 4.2 创建About页面
• 4.3 使用next/router和next/link构建导航组件
5 图片引用
• 5.1 方法一:使用原生<img>标签引入图片
• 5.2 方法二:使用next/image引用图片
6 生成静态化网站(SSG)
• 6.1 设置SSG的export命令
• 6.2 设置静态资源的basePath
• 6.3 设置SSG export输出的目录名称
7 接口请求
• 7.1 CSR/SSR/SSG 三种API请求方式
• 7.2 搭建服务端API服务
• 7.3 构建Profile页面
• 7.4 getServerSideProps和getStaticProps小节
• 7.5 搭建Next.js API Routers服务(选读)
8 动态路由
9 使用CLI命令动态生成目录
10 其他说明
11 项目Git源码
本次分享Demo的主要依赖包版本:
Node.js 16.16.0
next 12.2.5
react 18.2.0
react-dom 18.2.0
axios 0.27.2
※注:
代码区域每行开头的:
"+" 表示新增
"-" 表示删除
"M" 表示修改
1 创建Next.js项目
1.1 安装Next.js
找个合适的目录,执行:
npx create-next-app next-ssg
next-ssg是项目名称,可根据需要自行更改。
安装完成后,进入next-ssg,运行:
yarn dev
浏览器打开http://localhost:3000/,项目运行成功。
1.2 设置项目目录
Next.js官方脚手架初始目录结构如下:
├─ /.next <-- 用于SSR运行的工程,执行yarn dev或yarn build后才会出现
├─ /node_modules
├─ /pages <-- Next.js指定的页面目录
| ├─ /api <-- Next.js指定的API服务目录,可以删除
| | └─ hello.js <-- API服务的hello接口
| ├─ _app.js <-- Next.js指定的项目入口文件
| └─ index.js <-- 项目首页
├─ /public <-- 静态目录,放在这里的文件可通过"/"直接访问(没有public这一层级)
| ├─ favicon.ico
| └─ vercel.svg
├─ /styles <-- 项目全局样式
| ├─ globals.css
| └─ Home.module.css <-- Home组件样式
├─ .eslintrc.json
├─ .gitignore
├─ next.config.js <-- Next.js配置文件
├─ package.json
├─ README.md
└─ yarn.lock
以上目录结构并没有看到src目录,这与日常项目的开发习惯不一致。如果希望保持一致的开发体验,仍然可以使用src做为开发目录。
按照以下步骤重新组织目录结构:
1. 停止项目运行
2. 在项目根目录新建src目录
3. 把pages、styles两个目录放到src目录里
4. 删除pages里的api目录(后续章节讲到API请求时再创建)
再执行yarn dev,项目依然正常运行。
为什么变更了目录结构,项目还可以正常运行?
Next.js的官方脚手架虽然没有src目录,但考虑到src目录是普遍存在于大多数脚手架工程中,所以Next.js也对src目录做了支持。当然,如果新建的不是src目录,把pages、styles放进去是无法被正确识别的。
关于src目录,官方的规则如下:
1. 如果根目录下有pages,则src/pages将被忽略。
2. public目录以及next.config.js、jsconfig.json、tsconfig.json不能放到src目录里。
官方说明:https://nextjs.org/docs/advanced-features/src-directory
1.3 项目入口文件
按以上目录设置后,项目的入口文件变为了src/pages/_app.js。稍后将结合演示项目进行具体讲解。
1.4 精简项目
修改src/pages/index.js,最简化页面:
function Index() {
return (
<h1>This is Index Page.</h1>
)
}
export default Index
修改src/pages/_app.js,删除首行的全局样式引用:
- import '../styles/globals.css'
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
对src目录及文件进行以下调整:
/src
+ ├─ /common <-- 公用目录
+ | ├─ /images <-- 公用图片目录
+ | └─ /styles <-- 公用样式目录
+ ├─ /components <-- 公用组件目录
├─ /pages <-- Next.js指定的页面目录
+ | ├─ /api <-- Next.js指定的API服务目录(不会生成api页面目录)
| ├─ _app.js <-- Next.js指定的项目入口文件(不会生成_app.html)
| └─ index.js <-- index页面(会生成index.html)
- └─ /styles <-- Next.js初始的样式目录(删除)
- └─ globals.css <-- Next.js初始的公用样式(删除)
现在,src目录结构如下,非常精简了:
/src
├─ /common <-- 公用目录
| ├─ /images <-- 公用图片目录
| └─ /styles <-- 公用样式目录
├─ /components <-- 公用组件目录
└─ /pages <-- Next.js指定的页面目录
├─ /api <-- Next.js指定的API服务目录(不会生成api页面目录)
├─ _app.js <-- Next.js指定的项目入口文件(不会生成_app.html)
└─ index.js <-- index页面(会生成index.html)
执行yarn dev,效果如下:
2 配置项目
2.1 设置路径别名
为了避免使用相对路径的麻烦,可以设置路径别名。
在项目根目录下创建jsconfig.json,代码如下:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
}
}
}
路径别名官方说明:https://nextjs.org/docs/advanced-features/module-path-aliases
这样在js代码开头的import路径中,直接使用@表示“src目录”,不用去数有多少个"../"了。
修改jsconfig.json需要重启项目才能生效。
2.2 配置SourceMap(不建议设置)
development环境是开启sourceMap的,production环境默认不开启sourceMap。
如果需要在production环境开启sourceMap,在next.config.js进行以下配置:
module.exports = {
productionBrowserSourceMaps: true,
}
为了不暴露项目源码,不建议进行以上设置。
2.3 设置页面title
设置页面的title很简单。
修改src/pages/_app.js:
src/_app.js:
+ import Head from 'next/head'
function MyApp({ Component, pageProps }) {
M return (
+ <>
+ <Head>
+ <title>My Next App</title>
+ </Head>
+ <Component {...pageProps} />
+ </>
+ )
}
export default MyApp
运行项目,发现页面的title已经修改成功。
2.4 设置HTML框架代码
在Next.js项目里有个public目录,但是里面并没有看到类似Create-React-App项目的index.html。那如何设置HTML的<head>内容呢?
新建src/pages/_document.js,代码如下:
import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html>
<Head>
<link rel="icon" href="/favicon.ico" />
<meta name="description" content="Next.js演示项目" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
_document.js也是Next.js的指定文件名,且必须在pages目录下才可生效。
你可能会好奇,既然_document.js可以设置<head>的内容,那为什么<title>却在第2.3章节的_app.js中设置呢?
这是因为_document.js只会在初始时进行预渲染。官方不建议把<title>放到_document.js中。如果你在_document.js中的<head>里设置了<title>,在build的时候会收到warning。
title规则官方说明:https://nextjs.org/docs/messages/no-document-title
执行yarn dev,在http://localhost:3000中打开浏览器调试工具,但是并没有看到_document.js设置的内容。
这是因为_document.js设置的内容在build后才会生效。dev模式是看不到刚刚设置的内容的。
2.5 以SSR模式运行项目
执行以下命令,build项目:
yarn build
执行后,在项目根目录下会生成一个.next的目录。这个目录就是用于运行SSR的代码,仅能运行在服务端,不能被浏览器直接运行。
然后再执行以下命令,以SSR模式运行项目:
yarn start
※注:每次代码更新,在执行yarn start之前,一定要先执行yarn build。否则运行的并不是最新build的项目。
现在打开http://localhost:3000,看到是SSR模式运行的项目。打开调试工具,看到_document.js设置的代码已生效:
yarn start默认是运行在3000端口,如果想运行在4000端口,可以执行以下命令:
yarn start -p 4000
更多Next.js CLI命令,可参阅官方说明。
Next.js CLI官方说明:https://nextjs.org/docs/api-reference/cli
2.6 设置404/500页面
继续回到dev模式,运行yarn dev。
现在打开一个并不存在的页面路径,例如http://localhost:3000/test,页面显示如下: