NPM version
Downloads
Repo Dependents
Github repo

这是一份快速参考备忘单,包含 Next.js 的 API 参考列表和一些示例

入门

创建项目

1
2
3
4
5
npx create-next-app@latest
# or
yarn create next-app
# or
pnpm create next-app

或者创建 TypeScript 项目

1
2
3
4
5
npx create-next-app@latest --typescript
# or
yarn create next-app --typescript
# or
pnpm create next-app --typescript

运行 npm run devyarn devpnpm dev 以在 http://localhost:3000 上启动开发服务器

添加首页

使用以下内容填充 pages/index.js

1
2
3
4
5
function HomePage() {
return <div>Welcome to Next.js!</div>
}

export default HomePage

Next.js 是围绕页面的概念构建的。 页面是从 pages 目录中的 .js.jsx.ts.tsx 文件导出的 React 组件

getServerSideProps

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Page({ data }) {
// 渲染数据...
}

// 每个请求都会调用它
export async function getServerSideProps() {
// 从外部 API 获取数据
const res = await fetch(`https://.../data`)
const data = await res.json()

// 通过 props 向页面传递数据
return { props: { data } }
}

export default Page

如果您从页面导出一个名为 getServerSideProps(服务器端渲染)的函数,Next.js 将使用 getServerSideProps 返回的数据在每个请求上预渲染该页面

  • 当您直接请求此页面时,getServerSideProps 在请求时运行,此页面将使用返回的 props 进行预渲染
  • 当您通过 next/linknext/router 在客户端页面转换上请求此页面时,Next.js 会向服务器发送 API 请求,服务器运行 getServerSideProps

getStaticPaths

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// pages/posts/[id].js
export async function getStaticPaths() {
// 当这是真的时(在预览环境中)不要预呈现任何静态页面(更快的构建,但更慢的初始页面加载)
if (process.env.SKIP_BUILD_STATIC_GENERATION) {
return {
paths: [],
fallback: 'blocking',
}
}

// 调用外部 API 端点以获取帖子
const res = await fetch('https://.../posts')
const posts = await res.json()

// 根据帖子获取我们要预渲染的路径 在生产环境中,预渲染所有页面
// (构建速度较慢,但初始页面加载速度较快)
const paths = posts.map((post) => ({
params: { id: post.id },
}))

// { fallback: false } 表示其他路由应该 404
return { paths, fallback: false }
}

如果页面具有动态路由并使用 getStaticProps,则需要定义要静态生成的路径列表

  • 数据来自无头 CMS
  • 数据来自数据库
  • 数据来自文件系统
  • 数据可以公开缓存(非用户特定)
  • 页面必须预渲染(用于 SEO)并且速度非常快 —— getStaticProps 生成 HTML 和 JSON 文件,这两种文件都可以由 CDN 缓存以提高性能

getStaticProps

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 帖子将在构建时由 getStaticProps() 填充
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li>{post.title}</li>
))}
</ul>
)
}

// 这个函数在服务器端的构建时被调用。
// 它不会在客户端调用,因此您甚至可以直接进行数据库查询。
export async function getStaticProps() {
// 调用外部 API 端点以获取帖子。 您可以使用任何数据获取库
const res = await fetch('https://.../posts')
const posts = await res.json()

// 通过返回 { props: { posts } },Blog 组件将在构建时接收 `posts` 作为 prop
return {
props: {
posts,
},
}
}

export default Blog

在服务器端的构建时被调用

增量静态再生

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}

// 这个函数在服务器端的构建时被调用
// 如果启用了重新验证并且有新请求进入,它可能会在无服务器功能上再次调用
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json()

return {
props: {
posts,
},
// Next.js 将尝试重新生成页面:
// - 当请求进来时
// - 最多每 10 秒一次
revalidate: 10, // 片刻之间
}
}

// 这个函数在服务器端的构建时被调用
// 如果尚未生成路径,则可能会在无服务器函数上再次调用它
export async function getStaticPaths() {
const res = await fetch('https://.../posts')
const posts = await res.json()

// 根据帖子获取我们要预渲染的路径
const paths = posts.map((post) => ({
params: { id: post.id },
}))

// 我们将在构建时仅预渲染这些路径
// { fallback: blocking } 如果路径不存在,服务器将按需呈现页面
return { paths, fallback: 'blocking' }
}

export default Blog
  • 在初始请求之后和 10 秒之前对页面的任何请求也会被缓存和即时
  • 在 10 秒窗口之后,下一个请求仍将显示缓存的(陈旧的)页面
  • Next.js 在后台触发页面的重新生成
  • 一旦页面生成成功,Next.js 将使缓存失效并显示更新后的页面。如果后台重新生成失败,旧页面仍将保持不变

使用 useEffect 客户端数据获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { useState, useEffect } from 'react'

function Profile() {
const [data, setData] = useState(null)
const [isLoading, setLoading] = useState(false)

useEffect(() => {
setLoading(true)
fetch('/api/profile-data')
.then((res) => res.json())
.then((data) => {
setData(data)
setLoading(false)
})
}, [])

if (isLoading) return <p>Loading...</p>
if (!data) return <p>No profile data</p>

return (
<div>
<h1>{data.name}</h1>
<p>{data.bio}</p>
</div>
)
}

使用 SWR 获取客户端数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import useSWR from 'swr'

const fetcher = (...args) => fetch(...args).then((res) => res.json())

function Profile() {
const { data, error } = useSWR('/api/profile-data', fetcher)

if (error) return <div>Failed to load</div>
if (!data) return <div>Loading...</div>

return (
<div>
<h1>{data.name}</h1>
<p>{data.bio}</p>
</div>
)
}

静态文件服务

Next.js 可以在根目录中名为 public 的文件夹下提供静态文件,如图像。 然后,您的代码可以从基本 URL (/) 开始引用 public 中的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import Image from 'next/image'

function Avatar() {
return (
<Image
src="/me.png"
alt="me"
width="64"
height="64"
/>
)
}

export default Avatar

支持的浏览器和功能

Next.js 支持零配置的现代浏览器

  • Chrome 64+
  • Edge 79+
  • Firefox 67+
  • Opera 51+
  • Safari 12+

Next.js 支持在 package.json 文件中配置 Browserslist

1
2
3
4
5
6
7
8
9
{
"browserslist": [
"chrome 64",
"edge 79",
"firefox 67",
"opera 51",
"safari 12"
]
}

内置 CSS 支持

添加全局样式表

如果不存在,请创建一个 pages/_app.js 文件。 然后,导入 styles.css 文件

1
2
3
4
5
6
7
8
import '../styles.css';

// 在新的“pages/_app.js”文件中需要此默认导出
export default function MyApp({
Component, pageProps
}) {
return <Component {...pageProps} />
}

例如,考虑以下名为 styles.css 的样式表

1
2
3
4
5
6
7
body {
font-family:
'SF Pro Text', 'SF Pro Icons',
'Helvetica Neue', 'Helvetica',
'Arial', sans-serif;
margin: 0 auto;
}

从 node_modules 导入样式

对于全局样式表,如 bootstrapnprogress,您应该在 pages/_app.js 中导入文件

1
2
3
4
5
6
7
8
// pages/_app.js
import 'bootstrap/dist/css/bootstrap.css'

export default function MyApp({
Component, pageProps
}) {
return <Component {...pageProps} />
}

Next.js 9.5.4 开始,您的应用程序中的任何地方都允许从 node_modules 导入 CSS 文件

添加组件级 CSS (CSS Modules)

您无需担心 .error {} 与任何其他 .css.module.css 文件!他将被生成 hash 名称

1
2
3
4
.error {
color: white;
background-color: red;
}

然后,创建 components/Button.js,导入并使用上面的 CSS 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import styles from './Button.module.css'

export function Button() {
return (
<button
type="button"
// 请注意error
// 是如何作为导入的styles对象的属性访问的
className={styles.error}
>
Destroy
</button>
)
}

Sass 支持

Next.js 允许您使用 .scss.sass 扩展名导入 Sass,可以通过 CSS 模块和 .module.scss.module.sass 扩展名使用组件级 Sass

1
$ npm install --save-dev sass

在使用 Next.js 的内置 Sass 支持之前,请务必安装 sass

自定义 Sass 选项

通过在 next.config.js 中使用 sassOptions 来实现配置 Sass 编译器。例如添加 includePaths

1
2
3
4
5
6
7
8
const path = require('path')

module.exports = {
sassOptions: {
includePaths:
[path.join(__dirname, 'styles')],
},
}

Sass 变量

1
2
3
4
5
6
/* variables.module.scss */
$primary-color: #64ff00;

:export {
primaryColor: $primary-color;
}

pages/_app.js 中导入 variables.module.scss

1
2
3
4
5
6
7
8
9
import variables from '../styles/variables.module.scss'

export default function MyApp({ Component, pageProps }) {
return (
<Layout color={variables.primaryColor}>
<Component {...pageProps} />
</Layout>
)
}

CSS-in-JS

最简单的一种是内联样式:

1
2
3
4
5
6
7
function HiThere() {
return (
<p style={{ color: 'red' }}>hi 这里</p>
)
}

export default HiThere

使用 styled-jsx 的组件如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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

当然,你也可以使用 styled-components

Layouts

基础示例

1
2
3
4
5
6
7
8
9
10
11
12
13
// components/layout.js
import Navbar from './navbar'
import Footer from './footer'

export default function Layout({ children }) {
return (
<>
<Navbar />
<main>{children}</main>
<Footer />
</>
)
}

带有自定义应用程序的单一共享布局

1
2
3
4
5
6
7
8
9
10
// pages/_app.js
import Layout from '../components/layout'

export default function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
)
}

使用 TypeScript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// pages/index.tsx
import type { ReactElement } from 'react'
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'
import type { NextPageWithLayout } from './_app'

const Page: NextPageWithLayout = () => {
return <p>hello world</p>
}

Page.getLayout = function getLayout(page: ReactElement) {
return (
<Layout>
<NestedLayout>{page}</NestedLayout>
</Layout>
)
}

export default Page
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// pages/_app.tsx

import type { ReactElement, ReactNode } from 'react'
import type { NextPage } from 'next'
import type { AppProps } from 'next/app'

export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
getLayout?: (page: ReactElement) => ReactNode
}

type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout
}

export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
// 使用在页面级别定义的布局(如果可用)
const getLayout = Component.getLayout ?? ((page) => page)

return getLayout(<Component {...pageProps} />)
}

每页布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// pages/index.js
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'

export default function Page() {
return (
/** Your content */
)
}

Page.getLayout = function getLayout(page) {
return (
<Layout>
<NestedLayout>{page}</NestedLayout>
</Layout>
)
}
1
2
3
4
5
6
// pages/_app.js
export default function MyApp({ Component, pageProps }) {
// 使用在页面级别定义的布局(如果可用)
const getLayout = Component.getLayout || ((page) => page)
return getLayout(<Component {...pageProps} />)
}

数据请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// components/layout.js
import useSWR from 'swr'
import Navbar from './navbar'
import Footer from './footer'

export default function Layout({ children }) {
const { data, error } = useSWR('/api/navigation', fetcher)
if (error) return <div>Failed to load</div>
if (!data) return <div>Loading...</div>
return (
<>
<Navbar links={data.links} />
<main>{children}</main>
<Footer />
</>
)
}

图片优化

本地图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import Image from 'next/image'
import profilePic from '../public/me.png'

function Home() {
return (
<>
<h1>My Homepage</h1>
<Image
src={profilePic}
alt="Picture of the author"
// width={500} 自动提供
// height={500} 自动提供
// blurDataURL="data:..." 自动提供
// placeholder="blur" // 加载时可选的模糊处理
/>
<p>Welcome to my homepage!</p>
</>
)
}

远程图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import Image from 'next/image'

export default function Home() {
return (
<>
<h1>My Homepage</h1>
<Image
src="/me.png"
alt="Picture of the author"
width={500}
height={500}
/>
<p>Welcome to my homepage!</p>
</>
)
}

要使用远程图像,src 属性应该是一个 URL 字符串,可以是相对的也可以是绝对的

Priority

您应该将优先级属性添加到将成为每个页面的 Largest Contentful Paint (LCP) 元素的图像。 这样做允许 Next.js 专门确定要加载的图像的优先级(例如,通过预加载标签或优先级提示),从而显着提高 LCP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import Image from 'next/image'

export default function Home() {
return (
<>
<h1>My Homepage</h1>
<Image
src="/me.png"
alt="Picture of the author"
width={500}
height={500}
priority
/>
<p>Welcome to my homepage!</p>
</>
)
}

优化字体

Google 字体

自动托管任何 Google 字体。 字体包含在部署中,并从与您的部署相同的域提供服务。 浏览器不会向 Google 发送任何请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// pages/_app.js
import { Inter } from '@next/font/google'

// 如果加载可变字体,则无需指定字体粗细
const inter = Inter({ subsets: ['latin'] })

export default function MyApp({
Component, pageProps
}) {
return (
<main className={inter.className}>
<Component {...pageProps} />
</main>
)
}

指定粗细

如果不能使用可变字体,则需要指定粗细:

{5}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// pages/_app.js
import { Roboto } from '@next/font/google'

const roboto = Roboto({
weight: '400',
subsets: ['latin'],
})

export default function MyApp({
Component, pageProps
}) {
return (
<main className={roboto.className}>
<Component {...pageProps} />
</main>
)
}

数组指定多个 weight 或 style

1
2
3
4
5
const roboto = Roboto({
weight: ['400', '700'],
style: ['normal', 'italic'],
subsets: ['latin'],
})

在 <head> 中应用字体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// pages/_app.js
import { Inter } from '@next/font/google'
const inter = Inter({ subsets: ['latin'] })
export default function MyApp({ Component, pageProps }) {
return (
<>
<style jsx global>{`
html {
font-family: ${inter.style.fontFamily};
}
`}</style>
<Component {...pageProps} />
</>
)
}

单页使用

1
2
3
4
5
6
7
8
9
10
11
12
// pages/index.js
import { Inter } from '@next/font/google'

const inter = Inter({ subsets: ['latin'] })

export default function Home() {
return (
<div className={inter.className}>
<p>Hello World</p>
</div>
)
}

指定一个子集

1
2
// pages/_app.js
const inter = Inter({ subsets: ['latin'] })

next.config.js 中全局使用所有字体

1
2
3
4
5
6
7
8
9
10
11
// next.config.js
module.exports = {
experimental: {
fontLoaders: [
{
loader: '@next/font/google',
options: { subsets: ['latin'] }
},
],
},
}

如果两者都配置,则使用函数调用中的子集

本地字体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// pages/_app.js
import localFont from '@next/font/local'

// 字体文件可以位于“pages”内
const myFont = localFont({
src: './my-font.woff2'
})

export default function MyApp({
Component, pageProps
}) {
return (
<main className={myFont.className}>
<Component {...pageProps} />
</main>
)
}

如果要为单个字体系列使用多个文件,src 可以是一个数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const roboto = localFont({
src: [
{
path: './Roboto-Regular.woff2',
weight: '400',
style: 'normal',
},
{
path: './Roboto-Italic.woff2',
weight: '400',
style: 'italic',
},
{
path: './Roboto-Bold.woff2',
weight: '700',
style: 'normal',
},
{
path: './Roboto-BoldItalic.woff2',
weight: '700',
style: 'italic',
},
],
})

使用 Tailwind CSS

1
2
3
4
5
6
7
8
9
10
11
12
13
// pages/_app.js
import { Inter } from '@next/font/google'
const inter = Inter({
subsets: ['latin'],
variable: '--font-inter',
});
export default function MyApp({ Component, pageProps }) {
return (
<main className={`${inter.variable} font-sans`}>
<Component {...pageProps} />
</main>
)
}

最后,将 CSS 变量添加到您的 Tailwind CSS 配置中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// tailwind.config.js
const { fontFamily } = require('tailwindcss/defaultTheme')

module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
],
theme: {
extend: {
fontFamily: {
sans: ['var(--font-inter)', ...fontFamily.sans],
},
},
},
plugins: [],
}

优化 Scripts

页面脚本

1
2
3
4
5
6
7
8
9
10
11
import Script from 'next/script'

export default function Dashboard() {
return (
<>
<Script
src="https://example.com/script.js"
/>
</>
)
}

App 脚本

要为所有路由加载第三方脚本,导入 next/script 并将脚本直接包含在 pages/_app.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import Script from 'next/script'

export default function MyApp({
Component, pageProps
}) {
return (
<>
<Script
src="https://example.com/script.js"
/>
<Component {...pageProps} />
</>
)
}

将脚本卸载到 Web Worker(实验性的)

此策略仍处于试验阶段,只有在 next.config.js 中启用了 nextScriptWorkers 标志时才能使用:

1
2
3
4
5
module.exports = {
experimental: {
nextScriptWorkers: true,
},
}

设置完成后,定义 strategy="worker" 将自动在您的应用程序中实例化 Partytown 并将脚本卸载到网络工作者

1
2
3
4
5
6
7
8
9
10
11
12
import Script from 'next/script'

export default function Home() {
return (
<>
<Script
src="https://example.com/script.js"
strategy="worker"
/>
</>
)
}

其他属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import Script from 'next/script'

export default function Page() {
return (
<>
<Script
src="https://example.com/script.js"
id="example-script"
nonce="XUENAJFW"
data-test="script"
/>
</>
)
}

内联脚本

1
2
3
<Script id="show-banner">
{`document.getElementById('banner').classList.remove('hidden')`}
</Script>
1
2
3
4
5
6
<Script
id="show-banner"
dangerouslySetInnerHTML={{
__html: `document.getElementById('banner').classList.remove('hidden')`,
}}
/>

执行附加代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import Script from 'next/script'

export default function Page() {
return (
<>
<Script
src="https://example.com/script.js"
onLoad={() => {
console.log('Script has loaded')
}}
/>
</>
)
}

ESLint

集成 ESLint

1
2
3
"scripts": {
"lint": "next lint"
}

然后运行 npm run lintyarn lint

1
2
3
4
5
6
7
8
yarn lint
# 你会看到这样的提示:
#
# ? 您想如何配置 ESLint?
#
# ❯ 基本配置 + Core Web Vitals 规则集(推荐)
# 基本配置
# None

Strict

Strict 严格配置:包括 Next.js 的基本 ESLint 配置以及更严格的 Core Web Vitals 规则集

1
2
3
{
"extends": "next/core-web-vitals"
}

Base 基础配置:包括 Next.js 的基本 ESLint 配置

1
2
3
{
"extends": "next"
}

项目的根目录中创建一个包含所选配置的 .eslintrc.json 文件

自定义设置

1
2
3
4
5
6
7
8
{
"extends": "next",
"settings": {
"next": {
"rootDir": "packages/my-app/"
}
}
}

rootDir 可以是路径(相对或绝对)、glob(即“packages/*/”)或路径和/或 glob 数组

对自定义目录和文件进行检查

1
2
3
4
5
module.exports = {
eslint: {
dirs: ['pages', 'utils'],
},
}

在生产构建期间(next build)仅在“pages”和“utils”目录上运行 ESLint,或者使用命令

1
$ next lint --dir pages --dir utils --file bar.js

禁用规则

您可以使用 .eslintrc 中的 rules 属性直接更改它们:

1
2
3
4
5
6
7
{
"extends": "next",
"rules": {
"react/no-unescaped-entities": "off",
"@next/next/no-page-custom-font": "off"
}
}

修改或禁用受支持的插件(reactreact-hooksnext)提供的任何规则

Core Web Vitals

1
2
3
{
"extends": "next/core-web-vitals"
}

Prettier

1
2
3
npm install -S eslint-config-prettier
# or
yarn add --dev eslint-config-prettier

1
2
3
{
"extends": ["next", "prettier"]
}

lint-staged

1
2
3
4
5
6
7
8
9
10
const path = require('path')

const buildEslintCommand = (filenames) =>
`next lint --fix --file ${filenames
.map((f) => path.relative(process.cwd(), f))
.join(' --file ')}`;

module.exports = {
'*.{js,jsx,ts,tsx}': [buildEslintCommand],
}

内容添加到项目根目录中的 .lintstagedrc.js 文件中,以指定 --file 标志

TypeScript

create-next-app

1
2
3
4
5
npx create-next-app@latest --ts
# or
yarn create next-app --typescript
# or
pnpm create next-app --ts

静态生成和服务端渲染

1
2
3
4
5
6
7
8
9
10
import { GetStaticProps, GetStaticPaths, GetServerSideProps } from 'next'
export const getStaticProps: GetStaticProps = async (context) => {
// ...
}
export const getStaticPaths: GetStaticPaths = async () => {
// ...
}
export const getServerSideProps: GetServerSideProps = async (context) => {
// ...
}

现有项目添加 ts 配置

1
touch tsconfig.json

您还可以通过在 next.config.js 文件中设置 typescript.tsconfigPath 属性来提供 tsconfig.json 文件的相对路径

API 路由

1
2
3
4
5
6
7
8
9
10
import type {
NextApiRequest, NextApiResponse
} from 'next'

export default (
req: NextApiRequest,
res: NextApiResponse
) => {
res.status(200).json({ name:'John Doe' })
}

您还可以键入响应数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import type {
NextApiRequest, NextApiResponse
} from 'next'

type Data = {
name: string
}

export default (
req: NextApiRequest,
res: NextApiResponse<Data>
) => {
res.status(200).json({ name:'John Doe' })
}

自定义应用

使用内置类型 AppProps 并将文件名更改为 ./pages/_app.tsx,如下所示:

1
2
3
4
5
6
7
import type { AppProps } from 'next/app'

export default function MyApp({
Component, pageProps
}: AppProps) {
return <Component {...pageProps} />
}

类型检查 next.config.js

1
2
3
4
5
6
7
8
9
// @ts-check
/**
* @type {import('next').NextConfig}
**/
const nextConfig = {
/* 配置选项在这里 */
}

module.exports = nextConfig

忽略 TypeScript 错误

{3}
1
2
3
4
5
module.exports = {
typescript: {
ignoreBuildErrors: true,
},
}

危险地允许生产构建成功完成,即使您的项目有类型错误

环境变量

加载环境变量

将环境变量从 .env.local 加载到 process.env

1
2
3
DB_HOST=localhost
DB_USER=myuser
DB_PASS=mypassword

使用环境变量

1
2
3
4
5
6
7
8
9
// pages/index.js
export async function getStaticProps() {
const db = await myDB.connect({
host: process.env.DB_HOST,
username: process.env.DB_USER,
password: process.env.DB_PASS,
})
// ...
}

自动扩展 .env* 文件中的变量

1
2
3
4
# .env
HOSTNAME=localhost
PORT=8080
HOST=http://$HOSTNAME:$PORT

如果您尝试使用实际值中带有 $ 的变量,则需要像这样对其进行转义:\$

1
2
3
4
5
6
7
8
# .env
A=abc

# becomes "preabc"
WRONG=pre$A

# becomes "pre$A"
CORRECT=pre\$A

向浏览器公开环境变量

为了向浏览器公开变量,您必须在变量前加上 NEXT_PUBLIC_ 前缀

1
NEXT_PUBLIC_ANALYTICS_ID=abcdefghijk

NEXT_PUBLIC_ANALYTICS_ID 可以在此处使用,因为它的前缀是 NEXT_PUBLIC_

1
2
3
4
5
6
7
8
9
10
11
12
// pages/index.js
import setupAnalyticsService from '../lib/my-analytics-service'

//
// 它将在构建时转换为 `setupAnalyticsService('abcdefghijk')`
setupAnalyticsService(process.env.NEXT_PUBLIC_ANALYTICS_ID)

function HomePage() {
return <h1>Hello World</h1>
}

export default HomePage

路由

介绍

路由器将自动将名为 index 的文件路由到目录的根目录

:–
pages/index.js /
pages/blog/index.js /blog

路由器支持嵌套文件。如果创建嵌套文件夹结构,文件将以同样的方式自动路由

:–
pages/blog/first-post.js /blog/first-post
pages/dashboard/settings/username.js /dashboard/settings/username

动态路由

:–
pages/blog/[slug].js /blog/:slug (/blog/hello-world)
pages/[username]/settings.js /:username/settings (/foo/settings)
pages/post/[...all].js /post/* (/post/2020/id/title)

具有动态路由的页面

如果您创建一个名为 pages/posts/[pid].js 的文件,那么它可以在 posts/1posts/2 等处访问

1
2
3
4
5
6
7
8
9
10
import { useRouter } from 'next/router'

const Post = () => {
const router = useRouter()
const { pid } = router.query

return <p>Post: {pid}</p>
}

export default Post

使用 useRouter 获取动态路由参数 pid

页面之间的链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import Link from 'next/link'

export default function Home() {
return (
<ul>
<li>
<Link href="/">首页</Link>
</li>
<li>
<Link href="/about">关于我们</Link>
</li>
<li>
<Link href="/blog/hello-world">
博文
</Link>
</li>
</ul>
)
}

:–
/ pages/index.js
/about pages/about.js
/blog/hello-world pages/blog/[slug].js

链接到动态路径

1
2
3
4
5
6
7
8
9
import Link from 'next/link'

export default function Posts({ posts }) {
return (
<Link href={`/blog/${encodeURIComponent(post.slug)}`}>
标题
</Link>
)
}

URL 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import Link from 'next/link'

export default function Posts({ posts }) {
return (
<Link
href={{
pathname: '/blog/[slug]',
query: { slug: posts.slug },
}}
>
标题
</Link>
)
}

动态路由

考虑以下页面 pages/post/[pid].js

1
2
3
4
5
6
7
8
9
10
import { useRouter } from 'next/router'

const Post = () => {
const router = useRouter()
const { pid } = router.query

return <p>Post: {pid}</p>
}

export default Post

到动态路由的客户端导航由 next/link 处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import Link from 'next/link'

export default function Home() {
return (
<div>
<Link href="/post/abc">
转到 pages/post/[pid].js
</Link>
<Link href="/post/abc?foo=bar">
也转到 pages/post/[pid].js
</Link>
<Link href="/post/abc/a-comment">
转到 pages/post/[pid]/[comment].js
</Link>
</div>
)
}

多个动态路由

工作方式相同。 页面 pages/post/[pid]/[comment].js 将匹配路由 /post/abc/a-comment 并且它的查询对象将是:

1
{ "pid": "abc", "comment": "a-comment" }

捕捉所有路由

可以通过在括号内添加三个点 (...) 来扩展动态路由以捕获所有路径,pages/post/[...slug].js 匹配 /post/a,也匹配 /post/a/b/post/a/b/c

1
2
3
4
5
// /post/a
{ "slug": ["a"] }

// /post/a/b
{ "slug": ["a", "b"] }

可选捕获所有路由

使用 [[...slug]]pages/post/[[...slug]].js 将匹配 /post/post/a/post/a/b

1
2
3
4
5
6
// GET `/post` (empty object)
{ }
// `GET /post/a` (single-element array)
{ "slug": ["a"] }
// `GET /post/a/b` (multi-element array)
{ "slug": ["a", "b"] }

事件执行调整页面

1
2
3
4
5
6
7
8
9
10
11
12
13
import { useRouter } from 'next/router'

export default function ReadMore() {
const router = useRouter()

return (
<button
onClick={() => router.push('/about')}
>
点击这里阅读更多
</button>
)
}

浅路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { useEffect } from 'react'
import { useRouter } from 'next/router'

// 当前网址为“/”
export default function Page() {
const router = useRouter()

useEffect(() => {
// 始终在第一次渲染后进行导航
router.push('/?counter=10', undefined, { shallow: true })
}, [])

useEffect(() => {
// counter 变了!
}, [router.query.counter])
}

注意事项

浅路由仅适用于当前页面中的 URL 更改。 例如,假设我们有另一个名为 pages/about.js 的页面,并且您运行以下命令:

1
router.push('/?counter=10', '/about?counter=10', { shallow: true })

由于这是一个新页面,它会卸载当前页面,加载新页面并等待数据获取,即使我们要求进行浅层路由

另见


评论
avatar
竹山一叶
技术分享 个人心得
Follow Me
公告
欢迎光临小站,这里是我日常工作和学习中收集和整理的总结,希望能对你有所帮助:)

本站的内容经过个人加工总结而来,也参考了网友们分享的资料,如有侵权,请第一时间联系我,我将及时进行修改或删除😊
目录
  1. 1. 入门
    1. 1.1. 创建项目
    2. 1.2. 添加首页
    3. 1.3. getServerSideProps
    4. 1.4. getStaticPaths
    5. 1.5. getStaticProps
    6. 1.6. 增量静态再生
    7. 1.7. 使用 useEffect 客户端数据获取
    8. 1.8. 使用 SWR 获取客户端数据
    9. 1.9. 静态文件服务
    10. 1.10. 支持的浏览器和功能
  2. 2. 内置 CSS 支持
    1. 2.1. 添加全局样式表
    2. 2.2. 从 node_modules 导入样式
    3. 2.3. 添加组件级 CSS (CSS Modules)
    4. 2.4. Sass 支持
    5. 2.5. 自定义 Sass 选项
      1. 2.5.1. Sass 变量
    6. 2.6. CSS-in-JS
  3. 3. Layouts
    1. 3.1. 基础示例
    2. 3.2. 带有自定义应用程序的单一共享布局
    3. 3.3. 使用 TypeScript
    4. 3.4. 每页布局
    5. 3.5. 数据请求
  4. 4. 图片优化
    1. 4.1. 本地图片
    2. 4.2. 远程图片
    3. 4.3. Priority
  5. 5. 优化字体
    1. 5.1. Google 字体
    2. 5.2. 指定粗细
    3. 5.3. 数组指定多个 weight 或 style
    4. 5.4. 在 <head> 中应用字体
    5. 5.5. 单页使用
    6. 5.6. 指定一个子集
    7. 5.7. 本地字体
    8. 5.8. 使用 Tailwind CSS
  6. 6. 优化 Scripts
    1. 6.1. 页面脚本
    2. 6.2. App 脚本
    3. 6.3. 将脚本卸载到 Web Worker(实验性的)
    4. 6.4. 其他属性
    5. 6.5. 内联脚本
    6. 6.6. 执行附加代码
  7. 7. ESLint
    1. 7.1. 集成 ESLint
    2. 7.2. Strict
    3. 7.3. 自定义设置
    4. 7.4. 对自定义目录和文件进行检查
    5. 7.5. 禁用规则
    6. 7.6. Core Web Vitals
    7. 7.7. Prettier
    8. 7.8. lint-staged
  8. 8. TypeScript
    1. 8.1. create-next-app
    2. 8.2. 静态生成和服务端渲染
    3. 8.3. 现有项目添加 ts 配置
    4. 8.4. API 路由
    5. 8.5. 自定义应用
    6. 8.6. 类型检查 next.config.js
    7. 8.7. 忽略 TypeScript 错误
  9. 9. 环境变量
    1. 9.1. 加载环境变量
    2. 9.2. 自动扩展 .env* 文件中的变量
    3. 9.3. 向浏览器公开环境变量
  10. 10. 路由
    1. 10.1. 介绍
    2. 10.2. 具有动态路由的页面
    3. 10.3. 页面之间的链接
    4. 10.4. 链接到动态路径
    5. 10.5. URL 对象
    6. 10.6. 动态路由
    7. 10.7. 多个动态路由
    8. 10.8. 捕捉所有路由
    9. 10.9. 可选捕获所有路由
    10. 10.10. 事件执行调整页面
    11. 10.11. 浅路由
      1. 10.11.1. 注意事项
  11. 11. 另见
最新文章
网站资讯
文章数目 :
436
已运行时间 :
本站总字数 :
431.5k
本站访客数 :
本站总访问量 :
最后更新时间 :
文章归档文章分类文章标签复制本文标题复制本文地址
随便逛逛