介绍
Next.js 是 React 服务端渲染框架,用于构建 SEO 优化的 SPA 应用程序,具有以下特点:
- 基于页面的路由系统,无需配置,并支持动态路由
- 支持两种渲染方式,静态生成(SSG)和服务器端渲染(SSR)。
- 自动代码拆分,优化页面加载速度。
- 支持静态导出,可将应用导出为静态网站。
- 内置 CSS 和 Sass ,并支持任何 CSS-in-JS 库。
- 开发环境内置热更新。
- 完全可扩展。
- 方案成熟,世界上很多最大的品牌都在使用Next.js。
基于页面的路由系统
创建页面
- 在 Next.js 中,页面是一个 React 组件,需要将其放置在 pages 文件夹中,每个页面都基于其文件名与路由关联,将文件添加到 pages 目录后会自动用作路由。
- 组件需要被默认导出。
- 组件文件中不需要引入 React。
页面跳转
-
Next.js 提供了 Link 组件来进行页面跳转,类似 SPA 应用程序的跳转方式,如果浏览器中的 JavaScript 被禁用则使用链接跳转。
-
Link 组件在生产环境中通过预取功能自动优化应用程序以获得最佳性能。
静态资源
Next.js 可以在应用程序根目录中的 public 文件夹下提供静态资源,例如图像、CSS文件等。
public 名称不能被更改,并且是提供静态资源的唯一目录。
public 文件夹中的静态文件不能与 pages 文件夹中的静态文件重名,因为 URL 上只能驻留一种资源,因此要保证公共文件夹中的文件路径和页面文件的路径是唯一的。
公用文件和页面文件之间的冲突示例:
public/
hello
pages/
hello.js
复制代码
无冲突的公共文件和页面文件:
public/
hello.txt
pages/
hello.js
复制代码
修改页面元数据
Next.js 公开来一个内置组件 Head ,通过 Head 组件可以修改元数据,并追加到页面到 header 中。
CSS 样式
Next.js 提供了三种方式使用 CSS,CSS in JS 、CSS Module、以及全局的 CSS 样式文件。
styled-jsx
Next.js 中内置来 styled-jsx,它是一个 CSS in JS 的库,允许在 React 组件中编写 CSS ,也可以使用现有的任何 CSS in JS 解决方案。使用 styled-jsx 如下所示:
function HelloWorld() {
return (
<div>
Hello world
<p>scoped!</p>
<style jsx>{`
p {
color: blue;
}
div {
background: red;
}
@media (max-width: 600px) {
div {
background: blue;
}
}
`}</style>
<style global jsx>{`
body {
background: black;
}
`}</style>
</div>
)
}
export default HelloWorld
复制代码
全局 CSS 样式
- 在项目的根目录下添加 styles 文件夹,并在其中添加 global.css,文件名称可以自定义。
- 在 _app.js 中使用 import 导入文件路径即可。
- 在生产环境中,所有的 CSS 文件都会自动合并为一个缩小版的 .css 文件,从而提高程序的性能。
CSS 模块
- Next.js 使用文件名约定来支持 CSS模块,例如 [name].module.css 。
- 使用组件级 CSS 的好处是 CSS模块会通过自动创建唯一的类名在本地范围内重新定义 CSS,无需担心在不同文件夹中使用相同 CSS 类名称冲突的问题。
- 在生产环境当中,所有的 CSS 模块文件将自动合并为很多缩小和代码分割的 .css 文件,这些文件代表应用程序中的热执行路径,从而提供程序性能,让页面渲染时加载的 css 文件最少。
Sass 支持
Next.js 允许同时使用 .scss 和 .sass 扩展名倒入 Sass,也可以通过 CSS 模块和 .module.scss 或 .module.sass 扩展名使用组件级 Sass。
在使用 Sass 之前,我们需要确保项目中已经安装了 Sass,安装方式如下:
npm install sass
复制代码
如果需要配置 Sass
编译器,可以在 next.config.js 中的 sassOptions
属性中进行配置。
预渲染
预渲染概述
- 预渲染是指数据和 HTML 的拼接在服务器端直接完成。
- 预渲染可以让 SEO 更加的友好。
- 预渲染可以无需允许 JavaScript 即可查看应用程序的UI,会带来更好的用户体验。
预渲染的两种方式
- 在 Next.js 中提供了两种方式来实现预渲染,静态生成和服务端渲染。
- 静态生成和服务端渲染的区别是渲染时机不同。
- 静态生成是在构建的时候生成 HTML,会存储到磁盘中,以后的每个请求都共用构建时生成好的 HTML。
- 服务端渲染会在请求时生成 HTML,每个请求都会重新生成 HTML。
无数据的静态生成
- 如果组件不需要在其他地方获取数据,直接进行静态生成。
有数据的静态生成
- 如果组件需要在其他地方获取数据,在构建时会预先获取组件需要的数据,然后对组件进行静态生成。
静态生成
- 在 Next.js 中,提供了 getStaticProps 方法来帮助组件获取静态生成所需要的数据,并用过 props 的方式将数据传递给组件,该方法是一个异步函数,需要在组件内部中进行导出。
- getStaticProps 方法是在服务器端运行,永远不会在客户端中运行,所以可以从文件系统、API、或者数据库中获取数据。
- 开发模式下,该方法会在每个请求上运行。
- 生产模式下,该方法会在构建时运行。
export const getStaticProps: GetStaticProps = async (context) => {
let { data: general } = await axios({
url: 'http://strapi.codeuin.com/general'
})
let { data: posts } = await axios({
url: 'http://strapi.codeuin.com/posts'
})
return { props: { general, posts } }
}
export default Blog
复制代码
基于路由的静态生成
- 基于路由参数生成 HTML页面,有多少个参数就会生成多少个 HTML页面。
- 在构建时,先获取用户可以访问的所有路由参数,在根据路由参数获取具体数据,然后根据数据生成静态 HTML。
// 返回用户可以访问到的所有路由参数
export const getStaticPaths: GetStaticPaths = async (context: any) => {
const res = await axios('http://strapi.codeuin.com/posts')
const posts = res.data
const paths = posts.map((post) => `/blog/post/${post.id}`)
// path: 返回固定格式的路由
// fallback:当用户访问的参数没有在当前函数中返回时,false 会显示 404 页面,true 不会显示
return { paths, fallback: false }
}
// 返回路由参数所对应的具体数据内容
export const getStaticProps: GetStaticProps = async (context: any) => {
const { data: post } = await axios({
url: `http://strapi.codeuin.com/posts/${context.params.id}`,
})
return {
props: { post }
}
}
复制代码
服务端渲染
- 如果我们需要预先获取数据, Next.js 提供了
getServerSideProps
方法,会在接收每个请求时构建 HTML,该过程在服务器端完成。 - 使用方式和
getStaticProps
类似。
API Routes
- API Routes 可以理解成客户端向服务端获取数据中的接口。
- Next.js 允许 React 开发者编写服务器端代码创建数据接口。
- 不要在
getStaticPaths
和getStaticProps
这两个函数中访问 API Routes,因为这两个函数本身就是在服务器端中运行的,可以直接写服务器端中的代码。 - 在
pages/api
文件夹内的任何文件都将被映射到/api/*
export default (req, res) => {
res.status(200).json({ code: 200, data: 'Hello World!' })
}
复制代码
部署方式
NodeJS 服务器
Next.js 可以部署到任何支持 Node.js 的托管供应商,也可以采用自定义服务器,在项目根目录下新建 server.js
文件,文件内容如下:
const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')
const fs = require('fs')
const path = require('path')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
const express = require('express')
const port = 8080
app.prepare().then(() => {
const server = express()
server.get('*', (req, res) => {
// Be sure to pass `true` as the second argument to `url.parse`.
// This tells it to parse the query portion of the URL.
const parsedUrl = parse(req.url, true)
const { pathname, query } = parsedUrl
handle(req, res, parsedUrl)
})
const httpServer = createServer(server)
httpServer.listen(port, (err) => {
if (err) throw err
console.log('> Ready on http://localhost:8080')
})
}).catch((e) => {
// 写入错误日志
fs.writeFileSync(path.resolve(__dirname, 'logs/error_logs.txt'), e.stack);
console.error(e.stack)
process.exit(1)
})
复制代码
然后在 package.json
文件中的 scripts
新增 build
和 start
脚本:
{
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
}
}
复制代码
使用 next build
命令会在 .next 文件夹中构建生产环境的应用程序,构建之后, 使用 next start
命令可启动一个支持混合页面的 Node.js 服务器,该服务器同时服务于静态生成的页面和服务端呈现的页面。
Docker 镜像部署
Next.js 可以部署到任何支持 Docker
容器的托管厂商,如果要使用 Docker
方式部署,需要在项目的根目录下新建 Dockerfile
文件,文件内容如下:
# build stage
FROM node:lts-alpine as build-stage
RUN mkdir -p /app
COPY . /app
WORKDIR /app
RUN npm config set registry https://registry.npm.taobao.org
RUN npm config set npm_config_sharp_dist_base_url https://npm.taobao.org/mirrors/sharp-libvips
RUN npm install
CMD [ "npm","start" ]
复制代码
通过 docker build . -t my-nextjs-app
来生成镜像,并通过 docker run -p 3000:3000 my-nextjs-app
命令来运行它
参考文档
Next.js 官网:nextjs.org
Next.js 中文网:www.nextjs.cn