创建Next.js应用程序
要从头开始使用 React 构建完整的 Web 应用程序,您需要考虑许多重要的细节:
- 代码必须使用 webpack 之类的打包器打包,并使用 Babel 之类的编译器进行转换。
- 您需要进行生产优化,例如代码拆分。
- 您可能希望静态预渲染某些页面以提高性能和 SEO。您可能还想使用服务器端渲染或客户端渲染。
- 您可能需要编写一些服务器端代码来将您的 React 应用程序连接到您的数据存储。
一个框架可以解决这些问题。但是这样的框架必须具有正确的抽象级别——否则它不会很有用。它还需要具有出色的“开发人员体验”,确保您和您的团队在编写代码时拥有令人惊叹的体验。
Next.js:反应框架
输入Next.js,即 React 框架。Next.js 为上述所有问题提供了解决方案。但更重要的是,它会让你和你的团队在构建 React 应用程序时进入成功的陷阱。
Next.js 旨在拥有一流的开发人员体验和许多内置功能,例如:
- 直观的基于页面的路由系统(支持动态路由)
- 预渲染,静态生成(SSG) 和服务器端渲染(SSR) 均支持每页
- 自动代码拆分以加快页面加载速度
- 具有优化预取的客户端路由
- 内置 CSS和Sass 支持,支持任何CSS-in-JS库
- 支持快速刷新的开发环境
- 使用无服务器函数构建 API 端点的API 路由
- 完全可扩展
Next.js 用于数以万计的面向生产的网站和 Web 应用程序,其中包括许多世界上最大的品牌。
设置
首先,让我们确保您的开发环境已准备就绪。
- 如果您没有安装Node.js,请从此处安装。您需要 Node.js 版本10.13或更高版本。
- 您将在本教程中使用自己的文本编辑器和终端应用程序。
如果您使用的是 Windows,我们建议您下载 Git for Windows并使用它附带的 Git Bash,它支持本教程中特定于 UNIX 的命令。适用于 Linux 的 Windows 子系统 (WSL)是另一种选择。
创建 Next.js 应用
要创建 Next.js 应用程序,请打开终端,cd 进入要在其中创建应用程序的目录,然后运行以下命令:
npx create-next-app nextjs-blog --use-npm --example "https://github.com/vercel/next-learn/tree/master/basics/learn-starter"
在后台,它使用名为 的工具create-next-app ,它为您引导 Next.js 应用程序。它通过标志使用这个模板。--example
如果它不起作用,请查看此页面。
运行开发服务器
您现在有一个名为nextjs-blog . 让我们cd 进入它:
cd nextjs-blog
然后,运行以下命令:
npm run dev
这会在端口3000上启动 Next.js 应用程序的“开发服务器”。
来到Next.js页面
当我们访问http://localhost:3000 时,会出现下面页面。
编辑页面
让我们尝试编辑起始页。
- 确保 Next.js 开发服务器仍在运行。
pages/index.js 使用您的文本编辑器打开。- 在标签下找到写着“Welcome to”
<h1> 的文字并将其更改为“Learn”。 - 保存文件。
保存文件后,浏览器会自动使用新文本更新页面:
Next.js 开发服务器启用了快速刷新。当您对文件进行更改时,Next.js 几乎会立即自动在浏览器中应用更改。无需刷新!这将帮助您快速迭代您的应用程序。
在页面之间导航
Next.js中的页面
pages 在 Next.js 中,页面是从目录中的文件导出的 React 组件。
页面根据其文件名与路由相关联。例如,在开发中:
pages/index.js 与/ 路线相关联。pages/posts/first-post.js 与/posts/first-post 路线相关联。
我们已经有了这个pages/index.js 文件,所以让我们创建pages/posts/first-post.js 来看看它是如何工作的。
创建一个新页面
在.posts_pages
first-post.js 在目录中创建一个名为的文件posts ,其内容如下:
export default function FirstPost() {
return <h1>First Post</h1>;
}
组件可以有任何名称,但您必须将其导出为default 导出。
现在,确保开发服务器正在运行并访问http://localhost:3000/posts/first-post 。您应该看到以下页面:
这就是在 Next.js 中创建不同页面的方法。
pages 只需在目录下创建一个JS文件,文件的路径就变成了URL路径。
在某种程度上,这类似于使用 HTML 或 PHP 文件构建网站。您无需编写 HTML,而是编写 JSX 并使用 React 组件。
让我们添加一个指向新添加页面的链接,以便我们可以从主页导航到它。
链接组件
在网站上的页面之间链接时,您使用<a> HTML 标记。
在 Next.js 中,您使用Link Component fromnext/link 来包装<a> 标签。<Link> 允许您进行客户端导航到应用程序中的不同页面。
使用<Link>
首先,打开,并通过在顶部添加以下行来pages/index.js 导入Link 组件:next/link
import Link from 'next/link';
然后找到h1看起来像这样的标签:
<h1 className="title">
Learn <a href="https://nextjs.org">Next.js!</a>
</h1>
并将其更改为:
<h1 className="title">
Read{' '}
<Link href="/posts/first-post">
<a>this page!</a>
</Link>
</h1>
{' '} 添加一个空格,用于将文本分成多行。
接下来,打开pages/posts/first-post.js 并将其内容替换为以下内容:
import Link from 'next/link';
export default function FirstPost() {
return (
<>
<h1>First Post</h1>
<h2>
<Link href="/">
<a>Back to home</a>
</Link>
</h2>
</>
);
}
如您所见,该Link组件类似于使用标签,但您使用并在其中放置标签而不是 。
让我们看看它是否有效。您现在应该在每个页面上都有一个链接,允许您来回切换:
客户端导航
该Link 组件支持在同一个 Next.js 应用程序中的两个页面之间进行客户端导航。
客户端导航意味着使用 JavaScript进行页面转换,这比浏览器完成的默认导航要快。
这里有一个简单的方法可以验证它:
- 使用浏览器的开发者工具将
backgroundCSS 属性更改<html> 为yellow 。 - 单击链接可在两个页面之间来回切换。
- 您会看到黄色背景在页面转换之间持续存在。
这表明浏览器没有加载整个页面并且客户端导航正在工作。
如果您使用<a href="…"> 而不是<Link href="…"> 这样做,则背景颜色将在链接点击时被清除,因为浏览器会进行完全刷新。
代码拆分和预取
Next.js 会自动进行代码拆分,因此每个页面只加载该页面所需的内容。这意味着在呈现主页时,最初不会提供其他页面的代码。
即使您有数百个页面,这也可以确保主页快速加载。
仅加载您请求的页面的代码也意味着页面变得孤立。如果某个页面抛出错误,应用程序的其余部分仍然可以工作。
此外,在 Next.js 的生产版本中,每当Link 组件出现在浏览器的视口中时,Next.js 都会自动在后台预取链接页面的代码。当您单击链接时,目标页面的代码已经在后台加载,页面转换几乎是即时的!
概括
Next.js 通过代码拆分、客户端导航和预取(在生产中)自动优化您的应用程序以获得最佳性能。
您将路由创建为文件pages 并使用内置Link 组件。不需要路由库。
您可以在路由文档中的 API 参考和路由中了解有关Link 组件的更多信息。next/link
如果您需要链接到Next.js 应用程序之外的外部页面,只需使用<a> 不带Link .如果您需要添加属性,例如,className 将其添加到a 标签,而不是Link 标签。这是一个例子。
资产、元数据和 CSS
资产
Next.js 可以在顶级目录下提供静态资产,例如图片。可以从应用程序的根目录引用其中的文件,类似于.public public pages
该public 目录对于robots.txt 、Google Site Verification 和任何其他静态资产也很有用。查看静态文件服务的文档以了解更多信息。
下载您的个人资料图片
首先,让我们检索您的个人资料图片。
-
.jpg 以格式下载您的个人资料图片(或使用此文件)。 -
images 在目录中创建一个public 目录。 -
将图片另存为profile.jpg 目录public/images 。 -
图像大小可以在 400 像素 x 400 像素左右。 -
您可以直接删除该public 目录下未使用的 SVG 徽标文件。
未优化的图像
使用常规 HTML,您将添加您的个人资料图片,如下所示:
<img src="/images/profile.jpg" alt="Your Name" />
但是,这意味着您必须手动处理:
- 确保您的图像在不同的屏幕尺寸上响应
- 使用第三方工具或库优化您的图像
- 仅在图像进入视口时加载图像
和更多。相反,Next.js 提供了一个Image 开箱即用的组件来为您处理这个问题。
图像成分和图像优化
next/image 是 HTML<img> 元素的扩展,是为现代网络发展而来的。
Next.js 默认还支持图像优化。这允许在浏览器支持时以WebP等现代格式调整、优化和提供图像。这避免了将大图像传送到具有较小视口的设备。它还允许 Next.js 自动采用未来的图像格式并将它们提供给支持这些格式的浏览器。
自动图像优化适用于任何图像源。即使图像由外部数据源(如 CMS)托管,它仍然可以进行优化。
使用图像组件
Next.js 不是在构建时优化图像,而是在用户请求时按需优化图像。与静态站点生成器和纯静态解决方案不同,您的构建时间不会增加,无论是发送 10 个图像还是 1000 万个图像。
默认情况下,图像是延迟加载的。这意味着您的页面速度不会因视口之外的图像而受到惩罚。图像在滚动到视口时加载。
图像总是以这样的方式呈现,以避免累积布局移位,这是谷歌将在搜索排名中使用的核心网络生命力。
这是一个next/image 用于显示我们的个人资料图片的示例。height 和width 道具应该是所需的渲染大小,具有与源图像相同的纵横比。
**注意:**我们稍后将在“抛光布局”中使用该组件,无需复制它。
import Image from 'next/image';
const YourComponent = () => (
<Image
src="/images/profile.jpg" // Route of the image file
height={144} // Desired size with correct aspect ratio
width={144} // Desired size with correct aspect ratio
alt="Your Name"
/>
);
元数据
如果我们想修改页面的元数据,比如<title> HTML 标签,该怎么办?
<title> 是<head> HTML 标签的一部分,所以让我们深入研究如何<head> 在 Next.js 页面中修改标签。
在编辑器中打开pages/index.js 并找到以下行:
<Head>
<title>Create Next App</title>
<link rel="icon" href="/favicon.ico" />
</Head>
请注意,<Head> 使用 代替小写<head> 。<Head> 是一个内置在 Next.js 中的 React 组件。它允许您修改<head> 页面的。
Head 您可以从模块中导入组件next/head 。
添加Head 到first-post.js
我们还没有添加<title> 到我们的/posts/first-post 路线。让我们添加一个。
打开文件并在文件开头添加frompages/posts/first-post.js 的导入:Head next/head
import Head from 'next/head';
然后,更新导出的FirstPost 组件以包含该Head 组件。现在,我们将只添加title 标签:
export default function FirstPost() {
return (
<>
<Head>
<title>First Post</title>
</Head>
<h1>First Post</h1>
<h2>
<Link href="/">
<a>Back to home</a>
</Link>
</h2>
</>
);
}
第三方 JavaScript
第三方 JavaScript是指从第三方来源添加的任何脚本。通常,包含第三方脚本是为了将更新的功能引入不需要从头开始编写的站点,例如分析、广告和客户支持小部件。
添加第三方 JavaScript
让我们深入研究如何将第三方脚本添加到 Next.js 页面。
在编辑器中打开pages/posts/first-post.js 并找到以下行:
<Head>
<title>First Post</title>
</Head>
除了元数据,需要尽快加载和执行的脚本通常被添加到<head> 页面中。使用常规 HTML<script> 元素,将添加一个外部脚本,如下所示:
<Head>
<title>First Post</title>
<script src="https://connect.facebook.net/en_US/sdk.js" />
</Head>
该脚本包含Facebook SDK ,通常用于引入 Facebook 社交插件和其他功能。尽管这种方法有效,但以这种方式包含脚本并不能清楚地知道何时加载相对于在同一页面上获取的其他 JavaScript 代码。如果一个特定的脚本是渲染阻塞的并且会延迟页面内容的加载,这会显着影响性能。
使用脚本组件
next/script 是 HTML<script> 元素的扩展,并在获取和执行其他脚本时进行优化。
在同一个文件中,在文件开头添加一个 import for Script from next/script :
import Script from 'next/script';
现在,更新FirstPost 组件以包含该Script 组件:
export default function FirstPost() {
return (
<>
<Head>
<title>First Post</title>
</Head>
<Script
src="https://connect.facebook.net/en_US/sdk.js"
strategy="lazyOnload"
onLoad={() =>
console.log(`script loaded correctly, window.FB has been populated`)
}
/>
<h1>First Post</h1>
<h2>
<Link href="/">
<a>Back to home</a>
</Link>
</h2>
</>
);
}
请注意,脚本组件中定义了一些附加属性:
strategy 控制何时加载第三方脚本。值lazyOnload 告诉 Next.js 在浏览器空闲时间延迟加载此特定脚本onLoad 用于在脚本完成加载后立即运行任何 JavaScript 代码。在此示例中,我们将一条消息记录到控制台,指出脚本已正确加载
尝试访问http://localhost:3000/posts/first-post。通过使用浏览器的开发人员工具,您应该会在Console 面板中看到上面记录的消息。此外,您可以运行window.FB 以查看脚本已填充此全局变量。
注意: Facebook SDK 只是作为一个人为的示例来展示如何以高性能的方式将第三方脚本添加到您的应用程序中。现在您已了解在 Next.js 中包含第三方功能的基础知识,您可以FirstPost 在继续之前删除 Script 组件。
要了解有关该Script 组件的更多信息,请查看文档。
CSS样式
现在让我们谈谈CSS 样式。
如您所见,我们的索引页面 ( http://localhost:3000 ) 已经有了一些样式。如果您看一下pages/index.js ,您应该会看到如下代码:
<style jsx>{`
…
`}</style>
该页面使用了一个名为styled-jsx的库。它是一个“CSS-in-JS”库——它允许您在 React 组件中编写 CSS,并且 CSS 样式将被限定(其他组件不会受到影响)。
Next.js 内置了对styled-jsx的支持,但您也可以使用其他流行的 CSS-in-JS 库,例如styled-components或Emotion。
编写和导入 CSS
Next.js内置了对 CSS和 Sass 的支持,允许您导入.css 和.scss 文件。
还支持使用流行的 CSS 库,例如Tailwind CSS 。
在本课中,我们将讨论如何在 Next.js 中编写和导入 CSS 文件。我们还将讨论 Next.js 对CSS Modules和Sass的内置支持。让我们潜入吧!
上一页下一个
布局组件
首先,让我们创建一个Layout组件,它将在所有页面之间共享。
- 创建一个名为
components . - 在里面
components ,创建一个名为的文件layout.js ,内容如下:
export default function Layout({ children }) {
return <div>{children}</div>;
}
然后,打开pages/posts/first-post.js ,为组件添加一个导入Layout ,并将其设为最外层组件:
import Head from 'next/head';
import Link from 'next/link';
import Layout from '../../components/layout';
export default function FirstPost() {
return (
<Layout>
<Head>
<title>First Post</title>
</Head>
<h1>First Post</h1>
<h2>
<Link href="/">
<a>Back to home</a>
</Link>
</h2>
</Layout>
);
}
添加 CSS
现在,让我们为Layout 组件添加一些样式。为此,我们将使用CSS Modules,它允许您在 React 组件中导入 CSS 文件。
创建一个名为的文件components/layout.module.css ,其内容如下:
.container {
max-width: 36rem;
padding: 0 1rem;
margin: 3rem auto 6rem;
}
**重要提示:**要使用CSS 模块,CSS 文件名必须以.module.css .
要在里面使用这个container 类components/layout.js ,你需要:
- 导入 CSS 文件并为其分配一个名称,例如
styles styles.container 用作_className
打开components/layout.js 并将其内容替换为以下内容:
import styles from './layout.module.css';
export default function Layout({ children }) {
return <div className={styles.container}>{children}</div>;
}
如果您现在访问http://localhost:3000/posts/first-post,您应该会看到文本现在位于居中的容器中:
自动生成唯一的类名
现在,如果您查看浏览器的 devtools 中的 HTML,您会注意到组件div 呈现Layout 的类名如下所示layout_container__... :
这就是CSS 模块所做的:它会自动生成唯一的类名。只要您使用 CSS Modules,您就不必担心类名冲突。
此外,Next.js 的代码拆分功能也适用于CSS 模块。它确保为每个页面加载最少量的 CSS。这导致更小的包大小。
CSS 模块在构建时从 JavaScript 包中提取,并生成.css 由 Next.js 自动加载的文件。
全局样式
CSS 模块对于组件级样式很有用。但是,如果您希望每个页面都加载一些 CSS ,Next.js 也对此提供支持。
要加载全局 CSS文件,请创建一个名为的文件pages/_app.js ,其内容如下:
export default function App({ Component, pageProps }) {
return <Component {...pageProps} />;
}
该App 组件是所有不同页面通用的顶级组件。例如,您可以使用此App 组件在页面之间导航时保持状态。
重启开发服务器
**重要提示:**添加时需要重启开发服务器pages/_app.js 。按Ctrl + c停止服务器并运行:
npm run dev
添加全局 CSS
在 Next.js 中,您可以通过从pages/_app.js . 您不能在其他任何地方导入全局 CSS。
全局 CSS无法导入外部的原因pages/_app.js 是全局 CSS 会影响页面上的所有元素。
如果您要从主页导航到/posts/first-post 页面,则主页中的全局样式会/posts/first-post 无意中产生影响。
您可以将全局 CSS 文件放置在任何位置并使用任何名称。因此,让我们执行以下操作:
- 创建一个顶级
styles 目录并global.css 在里面创建。 - 将以下内容添加到
styles/global.css . 它会重置一些样式并更改a 标签的颜色:
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu,
Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
line-height: 1.6;
font-size: 18px;
}
* {
box-sizing: border-box;
}
a {
color: #0070f3;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
img {
max-width: 100%;
display: block;
}
最后,pages/_app.js 像这样打开添加导入 CSS 文件:
import '../styles/global.css';
export default function App({ Component, pageProps }) {
return <Component {...pageProps} />;
}
现在,如果您访问http://localhost:3000/posts/first-post,您将看到样式已应用:
如果它不起作用:确保在添加时重新启动开发服务器pages/_app.js 。
抛光布局
到目前为止,我们只添加了最少的 React 和 CSS 代码来说明诸如CSS 模块之类的概念。在我们继续下一节关于数据获取的课程之前,让我们完善一下我们的页面样式和代码。
更新components/layout.module.css
首先,打开components/layout.module.css 并用以下更精美的布局和个人资料图片样式替换其内容:
.container {
max-width: 36rem;
padding: 0 1rem;
margin: 3rem auto 6rem;
}
.header {
display: flex;
flex-direction: column;
align-items: center;
}
.backToHome {
margin: 3rem 0 0;
}
创造styles/utils.module.css
其次,让我们创建一组实用的 CSS 类,用于排版和其他将在多个组件中有用的类。
让我们添加一个名为styles/utils.module.css 以下内容的新 CSS 文件:
.heading2Xl {
font-size: 2.5rem;
line-height: 1.2;
font-weight: 800;
letter-spacing: -0.05rem;
margin: 1rem 0;
}
.headingXl {
font-size: 2rem;
line-height: 1.3;
font-weight: 800;
letter-spacing: -0.05rem;
margin: 1rem 0;
}
.headingLg {
font-size: 1.5rem;
line-height: 1.4;
margin: 1rem 0;
}
.headingMd {
font-size: 1.2rem;
line-height: 1.5;
}
.borderCircle {
border-radius: 9999px;
}
.colorInherit {
color: inherit;
}
.padding1px {
padding-top: 1px;
}
.list {
list-style: none;
padding: 0;
margin: 0;
}
.listItem {
margin: 0 0 1.25rem;
}
.lightText {
color: #666;
}
更新components/layout.js
第三,打开components/layout.js 并将其内容替换为以下代码,更改 Your Name 为实际名称:
import Head from 'next/head';
import Image from 'next/image';
import styles from './layout.module.css';
import utilStyles from '../styles/utils.module.css';
import Link from 'next/link';
const name = 'Your Name';
export const siteTitle = 'Next.js Sample Website';
export default function Layout({ children, home }) {
return (
<div className={styles.container}>
<Head>
<link rel="icon" href="/favicon.ico" />
<meta
name="description"
content="Learn how to build a personal website using Next.js"
/>
<meta
property="og:image"
content={`https://og-image.vercel.app/${encodeURI(
siteTitle,
)}.png?theme=light&md=0&fontSize=75px&images=https%3A%2F%2Fassets.vercel.com%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg`}
/>
<meta name="og:title" content={siteTitle} />
<meta name="twitter:card" content="summary_large_image" />
</Head>
<header className={styles.header}>
{home ? (
<>
<Image
priority
src="/images/profile.jpg"
className={utilStyles.borderCircle}
height={144}
width={144}
alt={name}
/>
<h1 className={utilStyles.heading2Xl}>{name}</h1>
</>
) : (
<>
<Link href="/">
<a>
<Image
priority
src="/images/profile.jpg"
className={utilStyles.borderCircle}
height={108}
width={108}
alt={name}
/>
</a>
</Link>
<h2 className={utilStyles.headingLg}>
<Link href="/">
<a className={utilStyles.colorInherit}>{name}</a>
</Link>
</h2>
</>
)}
</header>
<main>{children}</main>
{!home && (
<div className={styles.backToHome}>
<Link href="/">
<a>← Back to home</a>
</Link>
</div>
)}
</div>
);
}
以下是新内容:
meta 标签(如og:image ),用于描述页面的内容- 布尔
home 属性,它将调整标题和图像的大小 home 如果是,底部的“返回主页”链接false - 添加了带有 的图像
next/image ,这些图像预加载了优先级属性
import Head from 'next/head';
import Layout, { siteTitle } from '../components/layout';
import utilStyles from '../styles/utils.module.css';
export default function Home() {
return (
<Layout home>
<Head>
<title>{siteTitle}</title>
</Head>
<section className={utilStyles.headingMd}>
<p>[Your Self Introduction]</p>
<p>
(This is a sample website - you’ll be building a site like this on{' '}
<a href="https://nextjs.org/learn">our Next.js tutorial</a>.)
</p>
</section>
</Layout>
);
}
然后换成[Your Self Introduction] 你的自我介绍。以下是作者个人资料的示例:
就是这样!我们现在已经有了完善的布局代码,可以继续我们的数据获取课程。
造型技巧
以下是一些可能有用的样式提示。
您只需阅读以下部分即可。无需对我们的应用程序进行更改!
使用classnames 库切换类
classnames 是一个简单的库,可让您轻松切换类名。您可以使用npm install classnames 或安装它yarn add classnames 。
请查看其文档以获取更多详细信息,但这是基本用法:
- 假设您要创建一个
Alert 接受 的组件type ,它可以是'success' 或'error' 。 - 如果是
'success' ,则您希望文本颜色为绿色。如果是'error' ,您希望文本颜色为红色。
您可以先编写一个 CSS 模块(例如alert.module.css ),如下所示:
.success {
color: green;
}
.error {
color: red;
}
并classnames 像这样使用:
import styles from './alert.module.css';
import cn from 'classnames';
export default function Alert({ children, type }) {
return (
<div
className={cn({
[styles.success]: type === 'success',
[styles.error]: type === 'error',
})}
>
{children}
</div>
);
}
自定义 PostCSS 配置
开箱即用,无需配置,Next.js 使用 PostCSS 编译CSS。
要自定义 PostCSS 配置,您可以创建一个名为postcss.config.js . 如果您使用Tailwind CSS 之类的库,这将非常有用。
以下是添加Tailwind CSS的步骤。首先,安装软件包:
npm install -D tailwindcss autoprefixer postcss
然后,创建一个postcss.config.js :
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
我们还建议通过指定以下选项来配置内容源:content``tailwind.config.js
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
],
};
要了解有关自定义 PostCSS 配置的更多信息,请查看PostCSS 的文档。
要轻松开始使用 Tailwind CSS,请查看我们的示例。
使用 Sass
开箱即用,Next.js 允许您使用和扩展导入Sass 。你可以通过CSS 模块和or扩展来使用组件级 Sass 。.scss``.sass``.module.scss``.module.sass
在您可以使用 Next.js 的内置 Sass 支持之前,请务必安装sass :
npm install -D sass
预渲染和数据获取
预渲染
Next.js会提前为每个页面生成 HTML,而不是全部由客户端 JavaScript 完成。预渲染可以带来更好的性能和SEO。
每个生成的 HTML 都与该页面所需的最少 JavaScript 代码相关联。当浏览器加载页面时,其 JavaScript 代码将运行并使页面完全交互。(这个过程称为水合作用。)
两种形式的预渲染
Next.js 有两种预渲染形式:静态生成和服务器端渲染。不同之处在于它何时为页面生成 HTML。
- 静态生成是在构建时生成 HTML 的预渲染方法。然后在每个请求上重用预呈现的 HTML
- 服务器端渲染是在每个请求上生成 HTML 的预渲染方法。
在开发模式下(当您运行npm run dev 或时yarn dev ),每个页面都会在每个请求上预渲染——即使对于使用静态生成的页面也是如此。
每页基础
Next.js 允许选择用于每个页面的预渲染方式。可以通过对大多数页面使用静态生成并为其他页面使用服务器端渲染来创建“混合”Next.js 应用程序。
我们建议尽可能使用静态生成(有数据和无数据),因为您的页面可以构建一次并由 CDN 提供服务,这比让服务器在每次请求时呈现页面要快得多。
您可以将静态生成用于多种类型的页面,包括:
如果在用户请求之前需要预渲染此页面,那么就用静态生成。
如果用户不需要则的话使用静态生成会造成页面显示频繁更新数据,并且页面内容会随每个请求而更改,在这种情况下使用服务器端渲染。
有数据和无数据的静态生成
静态生成可以在有数据和没有数据的情况下完成。
到目前为止,我们创建的所有页面都不需要获取外部数据。当应用程序为生产而构建时,这些页面将自动静态生成。
但是,对于某些页面,如果不先获取一些外部数据,您可能无法呈现 HTML。也许您需要在构建时访问文件系统、获取外部 API 或查询数据库。Next.js 开箱即用地支持这种情况——带数据的静态生成。
使用数据进行静态生成getStaticProps
它是如何工作的?那么,在 Next.js 中,当你导出一个页面组件时,你也可以导出一个async 名为getStaticProps . 如果你这样做,那么:
export default function Home(props) { ... }
export async function getStaticProps() {
// Get external data from the file system, API, DB, etc.
const data = ...
// The value of the `props` key will be
// passed to the `Home` component
return {
props: ...
}
}
本质上,getStaticProps 它允许你告诉 Next.js:“嘿,这个页面有一些数据依赖——所以当你在构建时预渲染这个页面时,一定要先解决它们!”
注意:在开发模式下,getStaticProps 改为在每个请求上运行。
博客数据
我们现在将使用文件系统将博客数据添加到我们的应用程序中。每篇博文都将是一个降价文件。
- 创建一个名为的新顶级目录**
posts **(这与 不同pages/posts )。 - 在 内部
posts ,创建两个文件:pre-rendering.md 和ssg-ssr.md .
现在,将以下代码复制到posts/pre-rendering.md :
---
title: 'Two Forms of Pre-rendering'
date: '2020-01-01'
---
Next.js has two forms of pre-rendering: **Static Generation** and **Server-side Rendering**. The difference is in **when** it generates the HTML for a page.
- **Static Generation** is the pre-rendering method that generates the HTML at **build time**. The pre-rendered HTML is then _reused_ on each request.
- **Server-side Rendering** is the pre-rendering method that generates the HTML on **each request**.
Importantly, Next.js lets you **choose** which pre-rendering form to use for each page. You can create a "hybrid" Next.js app by using Static Generation for most pages and using Server-side Rendering for others.
然后,将以下代码复制到posts/ssg-ssr.md :
---
title: 'When to Use Static Generation v.s. Server-side Rendering'
date: '2020-01-02'
---
We recommend using **Static Generation** (with and without data) whenever possible because your page can be built once and served by CDN, which makes it much faster than having a server render the page on every request.
You can use Static Generation for many types of pages, including:
- Marketing pages
- Blog posts
- E-commerce product listings
- Help and documentation
You should ask yourself: "Can I pre-render this page **ahead** of a user's request?" If the answer is yes, then you should choose Static Generation.
On the other hand, Static Generation is **not** a good idea if you cannot pre-render a page ahead of a user's request. Maybe your page shows frequently updated data, and the page content changes on every request.
In that case, you can use **Server-Side Rendering**. It will be slower, but the pre-rendered page will always be up-to-date. Or you can skip pre-rendering and use client-side JavaScript to populate data.
您可能已经注意到,每个 Markdown 文件的顶部都有一个元数据部分,其中包含title 和date . 这称为 YAML Front Matter,可以使用名为gray-matter的库进行解析。
解析博客数据getStaticProps
pages/index.js 现在,让我们使用这些数据更新我们的索引页面 ( )。我们想:
- 解析每个 markdown 文件并获取
title 、date 和文件名(将用作id 发布 URL)。 - 列出索引页上的数据,按日期排序。
要在预渲染中执行此操作,我们需要实现getStaticProps .
实现 getStaticProps
首先,安装gray-matter,它可以让我们解析每个 markdown 文件中的元数据。
npm install gray-matter
接下来,我们将创建一个简单的库,用于从文件系统中获取数据。
- 创建一个名为
lib - 在里面
lib ,创建一个名为的文件posts.js ,内容如下
import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';
const postsDirectory = path.join(process.cwd(),'posts');//process.cwd()此方法不接受任何参数,此方法返回一个字符串,该字符串指定node.js进程的当前工作目录。
export function getSortedPostsData() {
// 在/posts下获取文件名
const fileNames = fs.readdirSync(postsDirectory);
const allPostsData = fileNames.map((fileName) => {
// 从文件名中删除“.md”以获取id
const id = fileName.replace(/\.md$/,'');
// 将标记文件读取为字符串
const fullPath = path.join(postsDirectory,fileName);
const fileContents = fs.readFileSync(fullPath,'utf8');
// 使用gray-matter分析post元数据部分
const matterResult = matter(fileContents);
console.log(matterResult.data);
// 将数据与id组合
return {
id,
...matterResult.data,
};
});
// 按日期对帖子排序
return allPostsData.sort(({date:a},{date:b}) => {
if (a < b) {
return 1;
} else if (a > b) {
return -1;
} else {
return 0;
}
})
}
**注意:**在 Next.js 中,lib 文件夹没有像pages 文件夹一样的指定名称,因此您可以将其命名为任何名称。通常习惯使用lib or utils 。
现在,我们需要添加一个 import for并在getSortedPostsData 里面调用它。getStaticProps pages/index.js
在您的编辑器中打开并在导出的组件pages/index.js 上方添加以下代码:Home
import { getSortedPostsData } from '../lib/posts';
export async function getStaticProps() {
const allPostsData = getSortedPostsData();
return {
props: {
allPostsData,
},
};
}
通过在 中的对象allPostsData 内部返回,博客文章将作为道具传递给组件。现在您可以像这样访问博客文章:props``getStaticProps``Home
export default function Home ({ allPostsData }) { ... }
要显示博客文章,让我们更新Home 组件以添加另一个<section> 标签,其中包含您的自我介绍部分下方的数据。不要忘记也将道具从更改() 为({ allPostsData }) :
export default function Home({ allPostsData }) {
return (
<Layout home>
{/* Keep the existing code here */}
{/* Add this <section> tag below the existing <section> tag */}
<section className={`${utilStyles.headingMd} ${utilStyles.padding1px}`}>
<h2 className={utilStyles.headingLg}>Blog</h2>
<ul className={utilStyles.list}>
{allPostsData.map(({ id, date, title }) => (
<li className={utilStyles.listItem} key={id}>
{title}
<br />
{id}
<br />
{date}
</li>
))}
</ul>
</section>
</Layout>
);
}
如果您访问http://localhost:3000 ,您现在应该会看到博客数据。
已经成功地(从文件系统)获取了外部数据并使用这些数据预渲染了索引页面。
getStaticProps 详细信息
以下是您应该了解的一些基本信息getStaticProps 。
获取外部 API 或查询数据库
在lib/posts.js 中,我们已经实现getSortedPostsData 了从文件系统中获取数据。但是您可以从其他来源获取数据,例如外部 API 端点,它会正常工作:
export async function getSortedPostsData() {
// Instead of the file system,
// fetch post data from an external API endpoint
const res = await fetch('..');
return res.json();
}
注意fetch() :客户端和服务器上的Next.js polyfills 。你不需要导入它。
也可以直接查询数据库:
import someDatabaseSDK from 'someDatabaseSDK'
const databaseClient = someDatabaseSDK.createClient(...)
export async function getSortedPostsData() {
// Instead of the file system,
// fetch post data from a database
return databaseClient.query('SELECT posts...')
}
这是可能的,因为getStaticProps 仅在服务器端运行。它永远不会在客户端运行。它甚至不会包含在浏览器的 JS 包中。这意味着您可以编写诸如直接数据库查询之类的代码,而无需将它们发送到浏览器。
开发与生产
因为它打算在构建时运行,所以您将无法使用仅在请求期间可用的数据,例如查询参数或 HTTP 标头。
仅允许在页面中
getStaticProps 只能从页面导出。您不能从非页面文件中导出它。
这种限制的原因之一是 React 需要在呈现页面之前拥有所有必需的数据。
如果我需要在请求时获取数据怎么办?
如果您不能在用户请求之前预渲染页面,则静态生成不是一个好主意。也许您的页面会显示频繁更新的数据,并且页面内容会随每个请求而更改。
在这种情况下,您可以尝试服务器端渲染或跳过预渲染。
在请求时获取数据
如果您需要在请求时而不是在构建时获取数据,您可以尝试服务器端渲染:
要使用服务器端渲染,您需要导出getServerSideProps 而不是getStaticProps 从页面导出。
使用getServerSideProps
这是getServerSideProps . 对于我们的博客示例来说,这不是必需的,因此我们不会实现它。
export async function getServerSideProps(context) {
return {
props: {
// props for your component
},
};
}
因为getServerSideProps 是在请求时调用的,所以它的参数(context )包含了请求特定的参数。
getServerSideProps 仅当您需要预渲染必须在请求时获取其数据的页面时才应使用。第一个字节的时间(TTFB)会比getStaticProps 因为服务器必须计算每个请求的结果要慢,并且如果没有额外的配置, CDN无法缓存结果。
客户端渲染
如果您不需要预先渲染数据,您还可以使用以下策略(称为Client-side Rendering):
- 静态生成(预渲染)页面的不需要外部数据的部分。
- 当页面加载时,使用 JavaScript 从客户端获取外部数据并填充剩余部分。
例如,这种方法适用于用户仪表板页面。因为仪表板是私有的、特定于用户的页面,所以 SEO 不相关,并且该页面不需要预渲染。数据经常更新,这需要在请求时获取数据。
驻波比
Next.js 背后的团队创建了一个名为SWR的用于数据获取的 React 钩子。如果您在客户端获取数据,我们强烈推荐它。它处理缓存、重新验证、焦点跟踪、间隔重新获取等。我们不会在这里详细介绍,但这里有一个示例用法:
import useSWR from 'swr';
function Profile() {
const { data, error } = useSWR('/api/user', fetch);
if (error) return <div>failed to load</div>;
if (!data) return <div>loading...</div>;
return <div>hello {data.name}!</div>;
}
查看SWR 文档以了解更多信息。
动态路线
页面路径取决于外部数据
在上一课中,我们介绍了页面内容依赖于外部数据的情况。我们曾经getStaticProps 获取所需的数据来呈现索引页面。
在本课中,我们将讨论每个页面路径都依赖于外部数据的情况。Next.js 允许您使用依赖于外部数据的路径静态生成页面。这会在 Next.js 中启用动态 URL 。
如何使用动态路由静态生成页面
在我们的例子中,我们想为博客文章创建动态路由:
- 我们希望每个帖子都有 path
/posts/<id> ,其中<id> 是顶级posts 目录下的 markdown 文件的名称。 - 既然我们有
ssg-ssr.md and pre-rendering.md ,我们希望路径是/posts/ssg-ssr and /posts/pre-rendering 。
步骤概述
我们可以通过以下步骤来做到这一点。您不必进行这些更改- 我们将在下一页完成所有操作。
首先,我们将创建一个名为**[id].js **under的页面pages/posts 。以开头[ 和结尾的页面] 是Next.js中的动态路由。
在pages/posts/[id].js 中,我们将编写代码来呈现一个帖子页面——就像我们创建的其他页面一样。
import Layout from '../../components/layout';
export default function Post() {
return <Layout>...</Layout>;
}
现在,这是新功能:我们将导出getStaticPaths 从此页面调用的异步函数。在这个函数中,我们需要返回一个可能值的列表id 。
import Layout from '../../components/layout';
export default function Post() {
return <Layout>...</Layout>;
}
export async function getStaticPaths() {
// Return a list of possible value for id
}
最后,我们需要getStaticProps 再次实现——这一次,使用给定的id . getStaticProps is given params ,其中包含id (因为文件名是[id].js )。
import Layout from '../../components/layout';
export default function Post() {
return <Layout>...</Layout>;
}
export async function getStaticPaths() {
// Return a list of possible value for id
}
export async function getStaticProps({ params }) {
// Fetch necessary data for the blog post using params.id
}
这是我们刚刚讨论的内容的图形摘要:
实现 getStaticPaths
首先,让我们设置文件:
- **
[id].js **在目录中创建一个名为的pages/posts 文件。 - 另外,在目录中删除
first-post.js pages/posts ——我们将不再使用它。
然后,pages/posts/[id].js 在您的编辑器中打开并粘贴以下代码。我们稍后会填写... :
import Layout from '../../components/layout';
export default function Post() {
return <Layout>...</Layout>;
}
然后,打开并在底部lib/posts.js 添加以下功能。getAllPostIds 它将返回目录中的文件名列表(不包括.md )posts :
export function getAllPostIds() {
const fileNames = fs.readdirSync(postsDirectory);
// Returns an array that looks like this:
// [
// {
// params: {
// id: 'ssg-ssr'
// }
// },
// {
// params: {
// id: 'pre-rendering'
// }
// }
// ]
return fileNames.map((fileName) => {
return {
params: {
id: fileName.replace(/\.md$/, ''),
},
};
});
}
重要提示:返回的列表不仅仅是一个字符串数组——它必须是一个看起来像上面注释的对象数组。每个对象都必须有params 密钥并包含一个带有id 密钥的对象(因为我们[id] 在文件名中使用)。否则,getStaticPaths 将失败。
最后,我们将导入该getAllPostIds 函数并在内部使用它getStaticPaths 。在导出的组件上方打开pages/posts/[id].js 并复制以下代码:Post
import { getAllPostIds } from '../../lib/posts';
export async function getStaticPaths() {
const paths = getAllPostIds();
return {
paths,
fallback: false,
};
}
实现 getStaticProps
我们需要获取必要的数据以使用给定的id .
为此,请再次打开并在底部lib/posts.js 添加以下功能。getPostData 它将基于以下内容返回帖子数据id :
export function getPostData(id) {
const fullPath = path.join(postsDirectory, `${id}.md`);
const fileContents = fs.readFileSync(fullPath, 'utf8');
const matterResult = matter(fileContents);
return {
id,
...matterResult.data,
};
}
然后,打开pages/posts/[id].js 并替换这一行:
import { getAllPostIds } from '../../lib/posts';
使用以下代码:
import { getAllPostIds, getPostData } from '../../lib/posts';
export async function getStaticProps({ params }) {
const postData = getPostData(params.id);
return {
props: {
postData,
},
};
}
帖子页面现在使用getPostData 函数 ingetStaticProps 来获取帖子数据并将其作为道具返回。
现在,让我们更新Post 要使用的组件postData 。用以下代码pages/posts/[id].js 替换导出的组件:Post
export default function Post({ postData }) {
return (
<Layout>
{postData.title}
<br />
{postData.id}
<br />
{postData.date}
</Layout>
);
}
就是这样!尝试访问这些页面:
您应该能够看到每个页面的博客数据:
渲染降价
为了呈现 markdown 内容,我们将使用该remark 库。首先,让我们安装它:
npm install remark remark-html
然后,打开lib/posts.js 并将以下导入添加到文件顶部:
import { remark } from 'remark';
import html from 'remark-html';
并按如下方式更新getPostData() 同一文件中的函数以使用remark :
export async function getPostData(id) {
const fullPath = path.join(postsDirectory, `${id}.md`);
const fileContents = fs.readFileSync(fullPath, 'utf8');
const matterResult = matter(fileContents);
const processedContent = await remark()
.use(html)
.process(matterResult.content);
const contentHtml = processedContent.toString();
return {
id,
contentHtml,
...matterResult.data,
};
}
重要提示:我们添加了**async **关键字 togetPostData 是因为我们需要使用await for remark 。async /允许您异步await 获取数据。
这意味着我们需要在调用时更新getStaticProps inpages/posts/[id].js 才能使用:await``getPostData
export async function getStaticProps({ params }) {
const postData = await getPostData(params.id);
}
最后,更新Post 组件pages/posts/[id].js 以contentHtml 使用dangerouslySetInnerHTML :
export default function Post({ postData }) {
return (
<Layout>
{postData.title}
<br />
{postData.id}
<br />
{postData.date}
<br />
<div dangerouslySetInnerHTML={{ __html: postData.contentHtml }} />
</Layout>
);
}
再次尝试访问这些页面:
您现在应该可以看到博客内容:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jHoAo8Yf-1653549168982)(images/image-20220525112109950.png)]
打磨帖子页面
添加title 到帖子页面
在pages/posts/[id].js 中,让我们title 使用帖子数据添加标签。您需要next/head 在文件顶部添加一个导入,并title 通过更新组件来添加标签Post :
// Add this import
import Head from 'next/head';
export default function Post({ postData }) {
return (
<Layout>
{/* Add this <Head> tag */}
<Head>
<title>{postData.title}</title>
</Head>
{/* Keep the existing code here */}
</Layout>
);
}
格式化日期
要格式化日期,我们将使用date-fns 库。首先,安装它:
npm install date-fns
接下来,创建一个名为components/date.js 并添加以下Date 组件的文件:
import { parseISO, format } from 'date-fns';
export default function Date({ dateString }) {
const date = parseISO(dateString);
return <time dateTime={dateString}>{format(date, 'LLLL d, yyyy')}</time>;
}
注意:您可以在date-fns网站上查看不同的format() 字符串选项。
现在,打开,在文件顶部为组件pages/posts/[id].js 添加一个导入,然后使用它:Date``{postData.date}
// Add this import
import Date from '../../components/date';
export default function Post({ postData }) {
return (
<Layout>
{/* Keep the existing code here */}
{/* Replace {postData.date} with this */}
<Date dateString={postData.date} />
{/* Keep the existing code here */}
</Layout>
);
}
如果您访问http://localhost:3000/posts/pre-rendering,您现在应该会看到日期写为**“2020 年 1 月 1 日”**。
添加 CSS
最后,让我们使用之前添加的文件添加一些 CSS styles/utils.module.css 。打开pages/posts/[id].js ,然后为 CSS 文件添加导入,并将Post 组件替换为以下代码:
// Add this import at the top of the file
import utilStyles from '../../styles/utils.module.css';
export default function Post({ postData }) {
return (
<Layout>
<Head>
<title>{postData.title}</title>
</Head>
<article>
<h1 className={utilStyles.headingXl}>{postData.title}</h1>
<div className={utilStyles.lightText}>
<Date dateString={postData.date} />
</div>
<div dangerouslySetInnerHTML={{ __html: postData.contentHtml }} />
</article>
</Layout>
);
}
如果您访问http://localhost:3000/posts/pre-rendering,页面现在应该看起来更好一点:
润色索引页
接下来,让我们更新我们的索引页面 ( pages/index.js )。我们需要使用该Link 组件为每个帖子页面添加链接。
打开pages/index.js 并在文件顶部为 and 添加以下Link 导入Date :
import Link from 'next/link';
import Date from '../components/date';
Home 然后,在同一文件中组件的底部附近,将li 标记替换为以下内容:
<li className={utilStyles.listItem} key={id}>
<Link href={`/posts/${id}`}>
<a>{title}</a>
</Link>
<br />
<small className={utilStyles.lightText}>
<Date dateString={date} />
</small>
</li>
如果你去http://localhost:3000,页面现在有每篇文章的链接:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4VBSAht3-1653549168983)(images/image-20220525160057266.png)]
如果某些东西不起作用,请确保您的代码看起来像这样。
动态路线详情
以下是您应该了解的有关动态路由的一些基本信息。
获取外部 API 或查询数据库
Like getStaticProps ,getStaticPaths 可以从任何数据源获取数据。在我们的示例中,getAllPostIds (由 使用getStaticPaths )可以从外部 API 端点获取:
export async function getAllPostIds() {
// Instead of the file system,
// fetch post data from an external API endpoint
const res = await fetch('..');
const posts = await res.json();
return posts.map((post) => {
return {
params: {
id: post.id,
},
};
});
}
开发与生产
倒退
回想一下我们fallback: false 从getStaticPaths . 这是什么意思?
如果fallback 是false ,则任何未返回的路径getStaticPaths 都将导致404 页面。
如果fallback 是true ,则行为getStaticProps 变化:
- 从返回的路径
getStaticPaths 将在构建时呈现为 HTML。 - 在构建时尚未生成的路径不会导致 404 页面。相反,Next.js 将在对此类路径的第一次请求时提供页面的“回退”版本。
- 在后台,Next.js 会静态生成请求的路径。对同一路径的后续请求将为生成的页面提供服务,就像在构建时预渲染的其他页面一样。
如果fallback 是blocking ,那么新路径将在服务器端呈现getStaticProps ,并缓存以供将来的请求使用,因此每个路径仅发生一次。
这超出了我们课程的范围,但您可以在文档fallback: true 中了解更多信息。fallback: 'blocking' fallback
包罗万象的路线
... 通过在括号内添加三个点 ( ) 可以扩展动态路由以捕获所有路径。例如:
pages/posts/[...id].js 匹配/posts/a , 但也/posts/a/b ,/posts/a/b/c 等等。
如果你这样做, in getStaticPaths ,你必须返回一个数组作为id 键的值,如下所示:
return [
{
params: {
id: ['a', 'b', 'c'],
},
},
];
并且params.id 将是一个数组getStaticProps :
export async function getStaticProps({ params }) {
}
查看catch all routes 文档以了解更多信息。
路由器
如果你想访问 Next.js 路由器,你可以通过useRouter 从next/router .
404页
要创建自定义 404 页面,请创建pages/404.js . 该文件是在构建时静态生成的。
// pages/404.js
export default function Custom404() {
return <h1>404 - Page Not Found</h1>;
}
查看我们的错误页面文档以了解更多信息。
更多示例
我们已经创建了几个示例来说明getStaticProps 并且getStaticPaths - 查看它们的源代码以了解更多信息:
API路由
创建 API 路由
API 路由允许您在 Next.js 应用程序中创建 API 端点。您可以通过在具有以下格式的目录中创建一个函数来做到这一点:pages/api
export default function handler(req, res) {
}
在API Routes 文档中了解有关上述请求处理程序的更多信息。
它们可以部署为无服务器函数(也称为 Lambda)。
创建一个简单的 API 端点
让我们试试看。创建一个使用以下代码调用hello.js 的文件:pages/api
export default function handler(req, res) {
res.status(200).json({ text: 'Hello' });
}
尝试在http://localhost:3000/api/hello访问它。你应该看到{"text":"Hello"} 。注意:
就是这样!在结束本课之前,让我们在下一页讨论使用API 路由的一些技巧。
API路由详情
以下是您应该了解的有关API 路由的一些基本信息。
不要从getStaticProps 或获取 API 路由getStaticPaths
您不应从getStaticProps 或获取 API 路由getStaticPaths 。getStaticProps 相反,直接在or中编写服务器端代码getStaticPaths (或调用辅助函数)。
原因如下:getStaticProps 并且getStaticPaths 只在服务器端运行,永远不会在客户端运行。此外,这些功能不会包含在浏览器的 JS 包中。这意味着您可以编写诸如直接数据库查询之类的代码,而无需将它们发送到浏览器。阅读编写服务器端代码文档以了解更多信息。
一个好的用例:处理表单输入
API 路由的一个很好的用例是处理表单输入。例如,您可以在页面上创建一个表单并让它向POST 您的 API 路由发送请求。然后,您可以编写代码将其直接保存到数据库中。API Route 代码不会包含在您的客户端包中,因此您可以安全地编写服务器端代码。
export default function handler(req, res) {
const email = req.body.email;
}
预览模式
当您的页面从无头 CMS 获取数据时,静态生成非常有用。但是,当您在无头 CMS 上编写草稿并希望立即在页面上预览草稿时,这并不理想。您希望 Next.js 在请求时而不是构建时呈现这些页面,并获取草稿内容而不是发布的内容。您希望 Next.js 仅针对这种特定情况绕过静态生成。
Next.js 有一个名为Preview Mode的功能来解决上述问题,它利用API Routes。要了解有关它的更多信息,请查看我们的预览模式文档。
动态 API 路由
API 路由可以是动态的,就像常规页面一样。查看我们的动态 API 路由文档以了解更多信息。
|