Next.js 是 React 服务端渲染应用框架. 用于构建 SEO 友好的 SPA 应用.
创建: npm init next-app next-guide
运行: npm run dev
访问: localhost:3000
临时安装 create-next-app 用于创建 Next.js 项目.
创建页面
在 Next.js 中, 页面是被放置在 pages 文件夹中的 React 组件.
组件需要被默认导出.
组件文件中不需要引入 React.
页面地址与文件地址是对应的关系
页面跳转
静态资源
应用程序根目录中的 public 文件夹用于提供静态资源.
通过以下形式进行访问.
public/images/1.jpg -> /images/1.jpg
public/css/base.css -> /css/base.css
元数据是指:比如页面标题title
通过 Head 组件修改元数据.
在 Next.js 中内置了 styled-jsx, 它是一个 CSS-in-JS 库, 允许在 React 组件中编写 CSS, CSS 仅作用于组件内部.
通过使用 CSS 模块功能, 允许将组件的 CSS 样式编写在单独的 CSS 文件中.
CSS 模块约定样式文件的名称必须为"组件文件名称.module.css
_app.js (文件名字固定)
import '../styles.css';
export default function App ({Component, pageProps}) {
return <Component {...pageProps}/>
}
style.css
body {
font-size: 12px;
font-weight: bold;
background: tomato;
}
预渲染概述
预渲染的两种形式
两种预渲染方式的选择
无数据和有数据的静态生成
静态生成 getStaticProps
getStaticProps 方法的作用是获取组件静态生成需要的数据. 并通过 props 的方式将数据传递给组件.
该方法是一个异步函数, 需要在组件内部进行导出.
在开发模式下, getStaticProps 改为在每个请求上运行.
import Head from "next/head";
import styles 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={styles.demo}>List page works</div>
<div>{data}</div>
</>
);
}
export async function getStaticProps () {
let data = await read(join(process.cwd(), 'pages', '_app.js'), 'utf-8');
console.log('HELLO');
return {
props: {
data
}
}
}
服务器端渲染 getServerSideProps
如果采用服务器端渲染, 需要在组件中导出 getServerSideProps 方法.
import Head from "next/head";
import styles 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={styles.demo}>List page works</div>
<div>{data}</div>
</>
);
}
export async function getServerSideProps (context) {
console.log(context.query)
let data = await read(join(process.cwd(), 'pages', '_app.js'), 'utf-8');
console.log('HELLO');
return {
props: {
data
}
}
}
基于参数为页面组件生成HTML页面,有多少参数就生成多少HTML页面
在构建应用时, 先获取用户可以访问的所有路由参数, 再根据路由参数获取具体数据, 然后根据数据生成静态 HTML.
注: getStaticPaths 和 getStaticProps 只运行在服务器端,
永远不会运行在客户端, 甚至不会被打包到客户端
JavaScript 中, 意味着这里可以随意写服务器端代码, 比
如查询数据库
[id].js
import { useRouter } from 'next/router';
export default function Post ({data}) {
const router = useRouter();
if (router.isFallback) return <div>loading</div>;
return <div>
<span>{data.id}</span>
<span>{data.title}</span>
</div>
}
// 返回用户可以访问到的所有的路由参数
export async function getStaticPaths () {
return {
paths: [{params: {id: "1"}}, {params: {id: "2"}}],
fallback: true // 如果传递一个不存在的参数,拿这个参数静态生成
}
}
// 返回路由参数所对应的具体的数据
export async function getStaticProps ({params}) {
console.log('Hello');
const id = params.id;
let data;
switch (id) {
case "1":
data = {id: "1", title: 'Hello'};
break;
case "2":
data = {id: "2", title: 'world'};
break;
case "3":
data = {id: "3", title: 'hello world'};
break;
default:
data = {}
}
return {
props: {
data
}
}
}
要创建自定义 404 页面, 需要在 pages 文件夹中创建 404.js 文件
API Routes 可以理解为接口, 客户端向服务器端发送请求获取数据的接口.
Next.js 应用允许 React 开发者编写服务器端代码创建数据接口.
如何实现 API Routes
在 pages/api 文件夹中创建 API Routes 文件. 比如 user.js
在文件中默认导出请求处理函数, 函数有两个参数, req 为请求对象, res 为响应对象
注: 当前 API Routes 可以接收任何 Http 请求方法
访问 API Routes: localhost:3000/api/user
不要在 getStaticPaths 或 getStaticProps 函数中访问 API Routes, 因为这两个函数就是在服务器端运行的, 可以直接写服务器端代码
npm init next-app movie
npm install @chakra-ui/core@next
npx chakra-cli init --theme
import { ChakraProvider, CSSReset } from "@chakra-ui/core";
import theme from "../chakra";
export default function App({ Component, pageProps }) {
return (
<ChakraProvider theme={theme}>
<CSSReset />
<Component {...pageProps} />
</ChakraProvider>
);
}
npm install react-icons --save
npm install @emotion/core @emotion/styled
npm install @emotion/babel-preset-css-prop --save-dev
{"presets": ["next/babel","@emotion/babel-preset-css-prop"]}
component/header.js
import { Button, Image, Box, Container } from "@chakra-ui/core";
import styled from "@emotion/styled";
import { css } from "@emotion/core";
import { FaSignInAlt, FaSearch } from "react-icons/fa";
import { BsFillPersonFill } from "react-icons/bs";
import Link from "next/link";
const logo = css`
width: 140px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
`;
const Search = styled.a`
float: right;
height: 52px;
border-right: 1px solid #393939;
border-left: 1px solid #393939;
color: #fff;
font-size: 16px;
padding: 0 15px;
display: flex;
align-items: center;
`;
const SignInAndJoin = styled.div`
height: 52px;
line-height: 52px;
color: #fff;
border-right: 1px solid #393939;
border-left: 1px solid #393939;
float: left;
padding: 0 6px;
& > button:nth-of-type(1):after {
content: "";
width: 1px;
height: 10px;
background: white;
position: absolute;
right: 0;
top: 7px;
}
`;
export default function Header() {
return (
<Box bg="#202020" h={52} borderBottom="1px solid #393939">
<Container maxW={1200} h={52} pos="relative">
<SignInAndJoin>
<Button
colorScheme="transparent"
size="xs"
leftIcon={<FaSignInAlt />}
>
登录
</Button>
<Button
colorScheme="transparent"
size="xs"
leftIcon={<BsFillPersonFill />}
>
注册
</Button>
</SignInAndJoin>
<Image css={logo} src="/images/logo.png"></Image>
<Link href="#">
<Search>
<FaSearch />
</Search>
</Link>
</Container>
</Box>
);
}
component/Navigation.js
import { Box, Stack } from '@chakra-ui/core';
import Link from 'next/link';
export default function Navigation() {
return (
<Box bg="#202020" h={52}>
<Stack
justifyContent="center"
alignItems="center"
color="white"
h={52}
direction="horizontal"
>
<Link href="#">
<a>影片</a>
</Link>
<Link href="#">
<a>漫画</a>
</Link>
<Link href="#">
<a>电影</a>
</Link>
<Link href="#">
<a>电视</a>
</Link>
<Link href="#">
<a>新闻</a>
</Link>
</Stack>
</Box>
);
}