1. 简介
Next.js 是基于React框架,具有服务端渲染功能。它快速且对SEO友好。使用Next.js,我们可以非常轻松的创建健壮的基于React的应用并对其进行测试。
1.1 为什么选择Next.js
- 零配置 - 自动编译并打包,从一开始就为生产环境而优化
- 混合模式 - SSG和SSR 在一个项目同时支持构建时预渲染页面(SSG)和请求时渲染页面(SSR)
- 增量静态生成 - 在构建之后以增量的方式添加并更新静态预渲染的页面
- 支持TypeScript - 自动配置并编译TypeScript
- 快速刷新 - 快速、可靠的实时编辑体验
- 基于文件系统的路由 - 每个pages目录下的组件都是一条路由
- API路由 - 创建API端点(可选)以提供后端功能
- 支持内置CSS - 使用CSS模块创建组件级样式。内置对sass的支持
- 代码拆分和打包 - 采用由Google Chrome小组创建的、并经过优化的打包和拆分算法
2. 快速开始
2.1 创建项目
npm init
项目初始化npm install next react react-dom
安装依赖package.json 的 scripts 字段中添加如下代码片段
配置next指令"dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint"
2.2 Next指令解析
- dev - 运行next dev,以开发模式启动Next.js
- build - 运行next build,以构建用于生产环境的应用程序
- start - 运行next start,以启动Next.js生产环境服务器
- lint - 运行next lint,以设置Next.js的内置ESlint配置
3. 快速开发
Next.js 是围绕着页面(pages)的概念构造的。一个页面(page)就是从一个pages目录下的.js、.jsx、.ts或.tsx文件导出(export)的React组件。
- 在next.js中创建一个pages目录,并创建一个包含以下内容的index.js文件
pages/index.js
function HomePage() {
return <div><h1>Welcome your first Next</h1></div>
}
export default HomePage
- 启动项目
npm run dev
,就可以在页面上看到对应的内容啦
3. Next.js 页面
在 Next.js 中,一个 page(页面) 就是一个从 .js 、 .jsx 、 .ts 或 .tsx 文件导出(export)的 React 组件 ,这些文件存放在 pages 目录下。每个 page(页面)都使用其文件名作为路由(route)。例如:
- pages/index.js 与 ‘/’ 路由链接
- pages/posts/first.js 与 ‘posts/first’ 路由相关联
页面间跳转,可以通过Link标签方式进行,通过href设置跳转地址,比如在其他页面中返回首页
pages/post/first.js
import Link from "next/link"; // 引入Link标签
function FirstPost() {
return (
<div>
<h2>这是邮局的第一封信</h2>
<Link href='/'> // 使用Link标签
返回首页
</Link>
</div>
)
}
export default FirstPost
4. 静态文件服务
Next.js支持静态文件(例如图片)存放在根目录public目录中,并对外提供访问。public目录下存放的静态文件对外访问路径以/
作为起始路径。在Next.js中,页面是一个React组件,从pages目录中导出,每个页面根据其文件名与一个路由相关联。
- 新建public文件件,并放入图片等静态文件信息
例如,在public/images/bz.jpg有一张图片,在页面中引用时:
<img src="/images/bz.jpg" alt=""/>
5. 元数据 (Meta Data)
在next.js中,我们可以借助内置的<Head>
React组件非常轻松地修改每个React页面的head部分。
- 引入Head标签
- 使用Head标签,通过内置title属性设置页面title值
代码示例:
xxx.js
import Head from "next/head"; // 引入head标签
function FirstPost() {
return (
<div>
<Head> // 页面中使用head
<title>
这是邮局的第一封信
</title>
</Head>
</div>
)
}
export default FirstPost
6. 对CSS的支持
在Next.js中,我们可以使用名为styled-jsx的内置css-in库。它允许在React组件中编写css,并且将这些样式作用于组件。
6.1 通过包裹子组件设置CSS
比如通过包含组件内容,来给组件设置一个公共的外壳样式container,这样在每个页面都可以用Container进行包裹,设置成对应的样式,比如设置PC端内容所占区域就可以使用该方式
- 新建components文件夹
- 在components文件夹中创建container.module.css, 用于设置样式
.container { width: 1280px; min-height: calc(100vh - 48px); border: 1px solid cornflowerblue; border-radius: 48px; padding: 24px; margin: 0 auto; }
- 在components文件夹中创建container.js,用于引用样式,并设置页面结构,如下示例则表示,给包裹在其中的页面内容设置container样式
import styles from './container.module.css' function Container({children}) { return <div className={styles.container}>{children}</div> } export default Container
- 在页面中进行使用,页面xxx.js,这样被Container包含的组件自然包含该样式
import Link from "next/link"; import Head from "next/head"; import Container from "../components/container"; // 引用Container样式模块 function HomePage() { return ( <Container> // 使用Container样式模块 <Head> <title>这是首页</title> </Head> <h1>Welcome your first Next</h1> <Link href='/post/first'>Post First</Link> </Container> ) } export default HomePage
6.2 给具体位置设置不同的样式
例如,我们有一个first.js页面,想要给该页面的标题设置一个单独的样式
- 在同等目录下,新建first.module.css文件
.title_red { color: #6C3838FF; }
- 在页面中引用该样式表
import First from './first.module.css' <h2 className={First.title_red}>这是邮局的第一封信</h2>
6.3 全局内置CSS样式
- 定义全局样式表 styles/styles.css
html, body { padding: 0; margin: 0; line-height: 1.6; font-size: 18px; background-color: lime; } * { box-sizing: border-box; } a { color: #0070f3; text-decoration: none; } a:hover { text-decoration: underline; } img { max-width: 100%; display: block; }
- 定义全局文件,应用全局样式 pages/_app.js
import '../styles/styles.css'
export default function App({Component, pageProps}){
return <Component {...pageProps} />
}
7. 预渲染
在Next.js中,我们知道它会为称为预渲染的页面生成HTML。Next.js中支持两种类型的预渲染:
- 静态生成 - 此方法在构建时生成HTML页面。这个预渲染的HTML在每个请求上面发送。这种方法对于营销网站、博客、电子商务产品列表网站、帮助、文档网站很有用
- 服务器端生成 - 此方法在每个请求上生成HTML页面。此方法适用于HTML页面内容可能随着某个请求而变化的情况
7.1 每个页面预渲染
Next.js允许为每个页面设置预渲染方法,其中大部分遵循静态生成,其他页面使用服务端渲染
7.2 无数据静态生成
静态生成可以在没有数据的情况下完成,在这种情况下,HTML页面将准备就绪,无需预取数据然后开始渲染。可以稍后或根据请求获取数据。这种技术有助于在没有任何数据的情况下向用户显示用户界面,以防获取数据需要一定的时间。
7.3 使用数据进行静态生成
静态生成可以用数据完成,在这种情况下,HTML页面在获取数据之前不会准备好,因为HTML可能会依赖于数据。每个组件都有一个特殊的方法getStaticProps可以用来获取数据并将数据作为页面的props传递,以便页面可以根据传递的props进行渲染。getStaticProps()函数在生产中的构建时运行,并在开发模式下每个请求都会运行
- 在获取请求数据时,根目录 pages/index.js 使用
getServerSideProps
方法,其他页面pages/xxx/sss.js使用getStaticProps
方法
pages/index.js中使用getServerSideProps
import Link from "next/link";
import Head from "next/head";
import Container from "../components/container";
function HomePage(props) {
return (
<Container>
<Head>
<title>这是首页</title>
</Head>
<h1>下面是应季水果清单:</h1>
<Link href='/post/first'>Post First</Link>
<ul>
{props.list.map((item, index) => (
<li key={index}>{item.fruitsname}</li>
))}
</ul>
</Container>
)
}
export async function getServerSideProps() { // 使用该方法发起网络请求,并将请求返回数据赋值给props
const res = await fetch('http://localhost:8090/api/fruits')
const resJson = await res.json()
return {
props: {
list: resJson.dataSource
}
}
}
export default HomePage
pages/xxx/sss.js中使用getStaticProps
import Link from "next/link";
import Head from "next/head";
import Container from "../../components/container";
import First from './first.module.css'
function FirstPost(props) {
return (
<Container>
<Head>
<title>
这是邮局的第一封信
</title>
</Head>
<h2 className={First.title_red}>这是邮局的第一封信</h2>
<Link href='/'>
返回首页
</Link>
||
<a href="https://www.baidu.com" target='_blank'>跳转外部的链接</a>
<ul>
{props.list.map((item, index) => {
return <li key={index}>{index}{item.fruitsname}</li>
})}
</ul>
</Container>
)
}
export async function getStaticProps() { // 使用该方法获取网络请求数据
const res = await fetch('http://localhost:8090/api/fruits')
const resJson = await res.json()
return { // 将从网络请求获取到的数据赋值给props的list列表
props: {
list: resJson.dataSource
}
}
}
export default FirstPost
8. 路由
8.1 路由信息汇总
Next.js使用基于文件系统的路由器,每当我们将任何页面添加到pages目录时,它都会自动通过url获得
-
index routes 文件夹中存在的index.js文件映射到目录的根目录
- pages/index.js 映射到 ‘/’
- pages/posts/index.js 映射到 ‘pages/posts’
-
nested routes 路由器支持嵌套路由,如果您创建嵌套文件夹结构,文件将自动以相同的结构定义路由
- pages/settings/dashboard/about.js 映射到“/settings/dashboard/about”
- pages/posts/first.js 映射到“/posts/first”
-
dynamic routes 我们也可以使用命名参数来匹配url
- pages/posts/[id].js 映射到“/posts/:id”,我们可以在其中使用“/posts/1”之类的 URL
- pages/[user]/settings.js 映射到“/posts/:user/settings”,我们可以在其中使用“/abc/settings”之类的 URL
- pages/posts/[…all].js 映射到“/posts/*”,我们可以在其中使用任何 URL,例如“/posts/2020/jun/”
8.2 动态路由
我们可以在首页定义一个list列表数据展示 pages/index.js
import Link from "next/link";
import Container from "../components/container";
function HomePage(props) {
return (
<Container>
<h1>下面是应季水果清单:</h1>
<Link href='/post/first'>Post First</Link>
<ul>
{props.list.map((item, index) => (
<li key={item.id}>
<Link href={`/fruits/${item.id}`}>{item.fruitsname}</Link>
</li>
))}
</ul>
</Container>
)
}
export async function getServerSideProps() {
const res = await fetch('http://localhost:8090/api/fruits')
const resJson = await res.json()
return {
props: {
list: resJson.dataSource
}
}
}
export default HomePage
跳转至详情页面后,定义对应的pages/fruits/[id].js,使用useRouter获取当前页面路由信息
import {useRouter} from "next/router";
function FruitPage() {
const router = useRouter()
return (
<>
<h3>这是水果{router?.query?.id}详情页面</h3>
</>
)
}
export default FruitPage
8.3 命令式路由
之前我们都是通过Link组件配置路径在页面上点击完成路由的跳转功能,还有另一种非HTML方式可以来实现这种功能——useRouter
import {useRouter} from "next/router";
function HomePage(props) {
const router = useRouter() // 在函数组件中获取router实例
function toPostPage() {
router.push('/post/first') // 通过router.push跳转至新的页面
}
return (
<Container>
<button style={{width: '200px',height: '48px'}} onClick={toPostPage}>点击跳转至Post</button>
</Container>
)
}
8.4 浅层路由
在 Next.js 中,浅层路由允许我们更改 URL 而无需再次运行数据获取方法,包括 getServerSideProps、getStaticProps 和 getInitialProps。要启用浅层路由,需要将 shallow 选项设置为 true。
- 例如,当我们在首页
/
,想要访问带有query参数的首页/?count=23
,此时我们设置了shallow选项为true,那么跳转后,不会再次发起网络请求,可以根据网络请求出console打印的内容即可了解调用的结果
import Container from "../components/container";
import {useRouter} from "next/router";
function HomePage(props) {
const router = useRouter()
function toCurpageQuery() {
router.push('/?count=65', undefined, {shallow: true})
}
return (
<Container>
<button onClick={toCurpageQuery}>跳转至当前页面,增加query参数</button>
</Container>
)
}
export async function getServerSideProps() {
const res = await fetch('http://localhost:8090/api/fruits')
const resJson = await res.json()
console.log('调用index的接口')
return {
props: {
list: resJson.dataSource
}
}
}
export default HomePage
9. Next Api路由
API路由(API routes)是一种使用Next.js创建REST API的方法。Next.js映射/pages/api文件夹中存在的任何文件,并将其视为api端点。API函数示例
export default (req, res) => {
...
}
- req - req 是 http.IncomingMessage 的一个实例,用于从请求中获取数据
- res - res 是 http.ServerResponse 的一个实例,用于发送数据作为相应
下面是一个简单的api设置方式
- 将api文件夹设置在pages目录下
- api文件夹中可以创建各种文件,例如:hobby.js
export default (req, res) => {
res.statusCode = 200
res.setHeader('Content-type', 'application/json')
res.end(JSON.stringify({slogan: '我爱篮球'})) // 使用JSON.stringify,不然网页识别有问题
}
此时,运行项目,访问地址http://localhost:3000/api/hobby
,即可看到我们所设置的api数据
9.1 中间件
Next.js为API路由提供了内置的中间件,用于解析传入的请求(req)。这些中间件是:
- req.cookie - 包含请求发送的cookie对象。默认为 {}
- req.query - 包含查询字符串的对象。默认为 {}
- req.body - 包含由content-type解析的正文的对象,如果没有发送正文,则为null
我们可以将上面的简单示例进行变形:
export default (req, res) => {
res.statusCode = 200
res.setHeader('Content-type', 'application/json')
res.end(JSON.stringify({query: req.query}))
}
此时访问: http://localhost:3000/api/hobby?counter=23
,就会看到页面打印的内容是{"query":{"counter":"23"}}
9.2 响应辅助办法 Response Helpers
res对象具有express.js之类的辅助办法,以简化开发来创建服务
- res.status(code) - 此方法设置响应的状态,传递的代码必须是有效的
- res.json(json) - 此方法返回json响应,传递的json对象必须是有效的json对象
使用示例:
export default (req, res) => {
res.status(200).json({slogan: '\'完蛋啦,页面没找到,下班吧\''})
}
注意事项:
- 动态路由处,next动态页面[id]方式命名文件,nuxt使用_id方式命名文件
- 在定义模板标签内容时,vue使用{{}},react使用{},react在内联样式style标签中设置样式时使用{{}}
- 无论是
http://localhost:3000/fruits/1
这种参数的1,还是http://localhost:3000/?count=65
这种参数的65,都在router中的query参数中,通过具体的id或者count来获取,不区分params和query
如果有用,点个赞呗~
总结用法,希望可以帮助到你,
我是Ably,你无须超越谁,只要超越昨天的自己就好~