1.Next.js 介绍
Next.js 是 React 服务端渲染应用框架,用于构建 SEO 友好的 SPA 应用
- 支持两种预渲染方式, 静态生成和服务器端渲染
- 基于页面的路由系统, 路由零配置
- 自动代码拆分. 优化页面加载速度
- 支持静态导出, 可将应用导出为静态网站
- 内置 CSS-in-JS 库 styled-jsx
- 方案成熟, 可用于生产环境, 世界许多公司都在使用
- 应用部署简单, 拥有专属部署环境 Vercel,也可以部署在其他环境
2.创建 Next.js 项目
创建:
npm init next-app next-guide
运行:
npm run dev
访问:
localhost:3000
临时安装
create-next- app
用于创建Next.js
项目。
3.基于页面的路由系统
1.创建页面
在Next.js中,页面是被放置在 pages 文件夹中的 React 组件
组件文件中不需要引入React并且需要被默认导出
页面地址与文件地址是对应的关系
// pages/list.js
export default function List () {
return (
<div>List Page</div>
)
}
访问地址是:http://localhost:3000/list
2. 页面跳转
页面与页面之间通过 Link 组件进行跳转
Link 组件默认使用 JavaScript 进行页面跳转. 即 SPA 形式的跳转
如果浏览器中 JavaScript 被禁用. 则使用链接跳转
Link 组件中不应添加除 href 属性以外的属性, 其余属性添加到 a 标签上
Link 组件通过预取(在生产中)功能自动优化应用程序以获得最佳性能
import Link from "next/link";
export default function Home() {
return (
<div>
Index Page works
<Link href="/list">
<a>Jump to List Page</a>
</Link>
</div>
);
}
4.静态资源、元数据和 CSS
1.静态资源
应用程序根目录中的 public 文件夹用于提供静态资源
通过以下形式进行访问:
/images/1.jpg => public/images/1.jpg /css/base.css => public/css/base.css
2.修改页面元数据
通过 Head 组件修改元数据:
import Head from "next/head";
export default function Home() {
return (
<>
<Head>
<title>index pages</title>
</Head>
</>
);
}
3. CSS 样式
3.1 内置 styled-jsx
在 Next.js 中 内置了 styled-jsx,它是一个 CSS-in-JS 库
它允许在 React 组件中编写 CSS,,CSS 仅作用于组件内部
import Head from "next/head";
import Link from "next/link";
export default function Home() {
return (
<>
<Head>
<title>Index Page</title>
</Head>
<div>
Index Page works
<Link href="/list">
<a className="demo">Jump to List Page</a>
</Link>
<img src="/images/1.jpeg" height="100" />
</div>
<style jsx>
{`
.demo {
color: red;
}
`}
</style>
</>
);
}
3.2 CSS模块
通过使用 CSS 模块功能,允许将组件的 CSS 样式编写在单独的 CSS 文件中
CSS 模块约定样式文件的名称必须为
"组件文件名称.module.css"
// pages/list.js
import Head from "next/head";
import style from "./list.module.css";
export default function List() {
return (
<>
<Head>
<title>List Page</title>
</Head>
<div className={style.demo}>List Page</div>
</>
);
}
// page/list.module.css
.demo {
color: tomato;
}
3.3 全局样式文件
- 在 pages 文件夹中新建
_app.js
文件并加入如下代码- 在项目根目录下创建 styles 文件夹,并在其中创建 global.css
- 在 _app.js 中通过 import 引入 global.css.
- 重新启动开发服务器
// pages/_app.js
import "../global.css";
export default function App({ Component, pageProps }) {
return <Component {...pageProps} />;
}
/** global.css **/
body {
background-color: skyblue;
font-size: 14px;
}
5.预渲染
1.预渲染概述
预渲染是指数据和 HTML 的拼接在服务器端提前完成
预渲染可以使 SEO 更加友好
预渲染会带来更好的用户体验,可以无需运行 JavaScript 即可查看应用程序 UI
2.预渲染的两种形式
在 Next.js 中支持两种形式的预渲染: 静态生成和服务器端渲染
静态生成和服务器端渲染是生成 HTML 的时机不同
静态生成:静态生成是在构建时生成 HTML ,以后的每个请求都共用构建时生成好的 HTML
服务器端渲染:服务器端渲染是在请求时生成 HTML ,每个请求都会重新生成 HTML
3.两种预渲染方式的选择
Next.js 允许开发者为每个页面选择不同的预渲染方式, 不同的预渲染方式拥有不同的特点, 应根据场景进行渲染
但建议大多数页面建议使用静态生成
静态生成一次构建,反复使用,访问速度快,因为页面都是事先生成好的
适用场景:营销页面、博客文章、电子商务产品列表、帮助和文档
服务器端渲染访问速度不如静态生成快,但是由于每次请求都会重新渲染,所以适用数据频繁更新的页面或页面内容随请求变化而变化的页面
4.无数据和有数据的静态生成
如果组件不需要在其他地方获取数据,直接进行静态生成
如果组件需要在其他地方获取数据, 在构建时 Next.js 会预先获取组件需要的数据,然后再对组件进行静态生成
5. 静态生成 getStaticProps
getStaticProps 方法的作用是获取组件静态生成需要的数据,并通过 props 的方式将数据传递给组件
该方法是一个异步函数,需要在组件内部进行导出
在开发模式下,getStaticProps 改为在每个请求上运行
// pages/list.js
import Head from "next/head";
import style from "./list.module.css";
import { readFile } from "fs";
import { promisify } from "util";
import { join } from "path";
const read = promisify(readFile);
export default function List({ data }) {
return (
<>
<Head>
<title>List Page</title>
</Head>
<div className={style.demo}>List Page</div>
<div>{data}</div>
</>
);
}
export async function getStaticProps() {
let data = await read(join(process.cwd(), "pages", "_app.js"), "utf-8");
console.log(data); // 会在 node 环境下输出,这个函数会在构建时运行
return {
props: {
data,
},
};
}
6. 服务器端渲染 getServerSideProps
如果采用服务器端渲染, 需要在组件中导出 getServerSideProps 方法
// pages/list.js
export async function getServerSideProps(context) {
// context 包含特定的请求参数如 query
console.log(context.query);
let data = await read(join(process.cwd(), "pages", "_app.js"), "utf-8");
return {
props: {
data,
},
};
}
运行 npm run build 之后,生产 .next 文件夹,可以看到 list 页面不会生成 HTML 页面
然后运行 npm start 启动生产环境的代码,访问 /list 页面, node 控制台会输出 getServerSideProps 方法中的打印语句
因为使用了 getServerSideProps 则表示采用服务端渲染,而不是静态生成,所以每次访问都会执行 getServerSideProps 方法
7.实现基于动态路由的静态生成
创建基于动态路由的页面组件文件, 命名时在文件名称外面加上[], 比如[id].js
导出异步函数 getStaticPaths, 用于获取所有用户可以访问的路由参数
export async function getStaticPaths () { // 此处获取所有用户可以访问的路由参数 return { // 返回固定合适的路由参数 paths: [{params: {id: 1}}, {params: {id: 2}}], // 当用户访问的路由有参数没有在当前函数中返回时,是否显示404页面, false表示显示,true 表示不显示 fallback: false } }
导出异步函数 getStaticProps, 用于根据路由参数获取具体的数据
export async function getStaticProps ({params}) { // params.id // 此处根据路由参数获取具体数据 return { // 将数据传递到组件中进行静态页面的生成 props: {} } }
注意:getStaticPaths 和 getStaticProps 只运行在服务器端,永远不会运行在客户端,甚至不会被打包到客户端 JavaScript 中,意味着这里可以随意写服务器端代码,比如查询数据库
8.自定义 404 页面
要创建自定义 404 页面,需要在 pages 文件夹中创建 404.js 文件
// 404.js
export default function Error () {
return <div>404 ~</div>
}
9.API Routes
API Routes 可以理解为接口,客户端向服务器端发送请求获取数据的接口
Next.js 应用允许 React 开发者编写服务器端代码创建数据接口
实现 API Routes:
- 在 pages/api 文件夹中创建 API Routes 文件,比如 user.js
- 在文件中默认导出请求处理函数,函数有两个参数,req 为请求对象,res 为响应对象
// 访问 API Routes: localhost:3000/api/user
// 不要在 getStaticPaths 或 getStaticProps 函数中访问 API Routes, 因为这两个函数就是在服务器端运行的,可以直接写服务器端代码
export default function (req, res) {
res.status(200).send({ id: 1, name: "yunmu" });
}
6.movie 项目
案例代码:React/next-movie · 云牧/exampleCode - 码云 - 开源中国 (gitee.com)
1.创建项目
npm init next-app movie
cd movie
npm install @chakra-ui/react @emotion/react @emotion/styled framer-motion
npm install react-icons
npm install @emotion/babel-preset-css-prop -D
npm install @babel/core
npm run dev
访问: localhost:3000
pages/_app.js
// import '../styles/globals.css'
import { ChakraProvider } from "@chakra-ui/react";
import theme from "@chakra-ui/theme";
function App({ Component, pageProps }) {
return (
<ChakraProvider theme={theme}>
<Component {...pageProps} />
</ChakraProvider>
);
}
export default App;
根目录创建 .babelrc 文件配置:
{
"presets": ["next/babel", "@emotion/babel-preset-css-prop"]
}
2.启动数据服务
找到对应项目中的 api 文件
npm install
npm run dev
数据服务地址IP: localhost:3005
在 axiosConfig.js 文件中导出
baseURL = 'http://localhost:3005'
3.编写代码
编写 Header 组件
编写 Navigation 组件
编写 Swiper 组件
编写 Movie 组件
编写 pages/detail/[id].js
4.生成静态文件
pages/detail/[id].js
// 获取到用户能够访问到的所有的路由参数
export async function getStaticPaths() {
// ["1", "2"]
let { data } = await axios.get("/api/videos", { baseURL });
// [{params: {id: "1"}}]
let paths = data.map((id) => ({ params: { id } }));
return {
paths,
fallback: false,
};
}
// 根据参数获取其对应的数据
export async function getStaticProps({ params }) {
let id = params.id;
let { data: detail } = await axios.get(`/api/detail?id=${id}`, { baseURL });
return {
props: {
detail,
},
};
}
index.js
import Layout from "../components/Layout";
import Swiper, { loadSwiper } from "../components/Swiper";
import Movie, { loadMovie } from "../components/Movie";
export default function Home({ swiper, movie }) {
return (
<Layout>
<Swiper data={swiper} />
<Movie data={movie} title="电影" />
</Layout>
);
}
export async function getStaticProps() {
// 获取轮播图数据
let { data: swiper } = await loadSwiper();
// 获取电影列表数据
let { data: movie } = await loadMovie();
return {
props: {
swiper,
movie,
},
};
}
然后根据数据渲染的对应的页面即可
最后需要导出静态网站:
"export": "next build && next export"
5.自定义 next 服务
package.json
"dev": "nodemon server/index.js",
server/index.js
const express = require('express')
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const app = next({dev})
const handler = app.getRequestHandler()
// prepare 方法是准备一下 next 应用
app.prepare().then(() => {
const server = express()
// 自定义路由系统
server.get('/hello', (req, res) => {
res.send('Hello Next.js')
})
// 使用内部基于页面的路由系统处理
server.get('*', (req, res) => {
handler(req, res)
})
server.listen(3000, () => console.log('服务器启动成功,请访问: http://localhost:3000'))
})
6. 部署到 Vercel
GitHub 上创建一个仓库,将这个项目代码导入到 GitHub 仓库
登陆 vercel.com/ 选择 GitHub 的这个仓库进行部署