2020年3月9日,星期一
Next.js 9.3
作者:我们很高兴今天推出 Next.js 9.3,其主要特性包括:
- 下一代静态站点生成 (SSG) 支持:通过新的数据获取方法内置优化静态生成。
- 预览模式:绕过静态生成的页面以显示来自 CMS 的草稿。
- 内置 Sass 支持用于全局样式表:应用程序现在可以直接导入
.scss
文件作为全局样式表。 - 内置 Sass CSS 模块支持用于组件级样式:利用
.module.scss
约定,局部作用域 CSS 可以导入并在应用程序中的任何位置使用。 - 404 页面自动静态优化:通过静态提供 404 页面来提高速度和可靠性。
- 运行时缩小 32 kB:通过强大的优化,所有 Next.js 应用程序的运行时大小都更小。
- Next.js GitHub 讨论社区:您现在可以在 GitHub 上的 Next.js 代码库中讨论和提问。
所有这些优势都是非破坏性的,并且完全向后兼容。您只需运行以下命令即可更新:
npm i next@latest react@latest react-dom@latest
下一代静态站点生成 (SSG) 支持
在构建网站或 Web 应用程序时,通常必须在两种策略之间进行选择:静态生成 (SSG) 或服务器端渲染 (SSR)。
Next.js 是第一个混合框架,允许您根据每个页面的具体情况选择最适合您用例的技术。
Next.js 9.0 引入了 自动静态优化的概念。当页面没有阻塞数据获取要求(例如 getInitialProps
)时,它将在构建时自动渲染为 HTML。
在更多情况下,您可能希望在构建时将页面渲染为静态 HTML,即使存在阻塞数据获取要求。例如,由 (无头) 内容管理系统 (CMS) 提供支持的营销页面或网站的博客部分。
我们与 SSG 和 next export
的重度用户(如 HashiCorp)进行了合作,并在 Next.js 历史上 评论最多的 RFC中与社区广泛讨论了正确的约束条件,以创建一种新的统一方式来进行数据获取和静态生成。
今天,我们非常激动地宣布两种新的数据获取方法:getStaticProps
和 getServerSideProps
。我们还提供了一种为动态路由静态生成静态页面的方法:getStaticPaths
。
这些新方法比 getInitialProps
模型具有许多优势,因为它们清楚地区分了哪些将成为 SSG 与 SSR。
-
getStaticProps
(静态生成):在构建时获取数据。 -
getStaticPaths
(静态生成):根据数据指定要预渲染的动态路由。 -
getServerSideProps
(服务器端渲染):在每次请求时获取数据。 -
这些改进是 API 的新增功能。所有新功能都完全向后兼容,并且可以逐步采用。没有引入弃用,并且
getInitialProps
将继续像现在一样工作。我们鼓励在新的页面和项目中采用这些新方法。
getStaticProps
如果从页面导出名为 getStaticProps
的异步函数,Next.js 将在构建时预渲染此页面。当您想要从 CMS 渲染特定的静态页面时,这尤其有用。
getStaticProps
始终在 Node.js 上下文中运行,并且代码会自动从浏览器捆绑包中进行树状抖动,以确保发送到浏览器的代码更少。这样,您就不必担心在 Node.js 和浏览器环境中执行数据获取代码,因为这两个环境之间存在一些不一致之处。
这允许您使用任何异步甚至同步数据获取技术,包括 fetch
、REST、GraphQL,甚至直接访问数据库。
export async function getStaticProps(context) {
return {
props: {}, // will be passed to the page component as props
};
}
context
参数是一个包含以下键的对象
params
:params
包含使用动态路由的页面的路由参数。例如,如果页面名称为[id].js
,则params
将如下所示:{ id: ... }
。要了解更多信息,请查看动态路由文档。您应该将此与稍后将解释的getStaticPaths
结合使用。
以下是一个使用 getStaticProps
从 CMS 获取博客文章列表的示例
// You can use any data fetching library
import fetch from 'node-fetch';
// posts will be populated at build time by getStaticProps()
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li>{post.title}</li>
))}
</ul>
);
}
// This function gets called at build time in the Node.js environment.
// It won't be called on client-side, so you can even do
// direct database queries. See the "Technical details" section.
export async function getStaticProps() {
// Call an external API endpoint to get posts.
const res = await fetch('https://.../posts');
const posts = await res.json();
// By returning { props: posts }, the Blog component
// will receive `posts` as a prop at build time
return {
props: {
posts,
},
};
}
export default Blog;
何时应使用 getStaticProps?
如果满足以下条件,则应使用 getStaticProps
:
- 渲染页面所需的数据在用户请求之前构建时可用。
- 数据来自无头 CMS。
- 数据可以公开缓存(非用户特定)。
- 页面必须预渲染(用于 SEO)并且速度非常快 -
getStaticProps
生成 HTML 和 JSON 文件,这两者都可以由 CDN 缓存以提高性能。
要了解更多关于getStaticProps
的信息,请参考数据获取文档。
getStaticPaths
如果一个页面具有动态路由并且使用了getStaticProps
,则需要定义一个路径列表,这些路径需要在构建时渲染成HTML。
如果你从使用动态路由的页面导出一个名为getStaticPaths
的async
函数,Next.js将静态预渲染getStaticPaths
指定的全部路径。
export async function getStaticPaths() {
return {
paths: [
{ params: { ... } } // See the "paths" section below
],
fallback: true or false // See the "fallback" section below
};
}
paths键(必需)
paths键决定了哪些路径将被预渲染。例如,假设你有一个使用名为pages/posts/[id].js
的动态路由的页面。如果你从这个页面导出getStaticPaths
并为paths返回以下内容
return {
paths: [
{ params: { id: 1 } },
{ params: { id: 2 } }
],
fallback: ...
}
那么Next.js将在构建时使用pages/posts/[id].js
中的页面组件静态生成posts/1
和posts/2
。
请注意,每个params
的值必须与页面名称中使用的参数匹配
- 如果页面名称为
pages/posts/[postId]/[commentId]
,则params
应包含postId
和commentId
。 - 如果页面名称使用通配符路由,例如
pages/[...slug]
,则params
应包含slug
,它是一个数组。例如,如果该数组为['foo', 'bar']
,则Next.js将在/foo/bar
处静态生成页面。
fallback键(必需)
getStaticPaths
返回的对象必须包含一个布尔类型的fallback键。
Fallback: false
如果fallback
为false
,则getStaticPaths
未返回的任何路径都将导致404页面。如果你知道所有路径在构建时都会已知,这将非常有用。
以下是一个示例,它为每个名为pages/posts/[id].js
的页面预渲染一个博客文章。博客文章列表将从CMS中获取,并由getStaticPaths
返回。然后,对于每个页面,它使用getStaticProps
从CMS中获取文章数据。
import fetch from 'node-fetch';
function Post({ post }) {
// Render post...
}
// This function gets called at build time
export async function getStaticPaths() {
// Call an external API endpoint to get posts
const res = await fetch('https://.../posts');
const posts = await res.json();
// Get the paths we want to pre-render based on posts
const paths = posts.map((post) => `/posts/${post.id}`);
// We'll pre-render only these paths at build time.
// { fallback: false } means other routes should 404.
return { paths, fallback: false };
}
// This also gets called at build time
export async function getStaticProps({ params }) {
// params contains the post `id`.
// If the route is like /posts/1, then params.id is 1
const res = await fetch(`https://.../posts/${params.id}`);
const post = await res.json();
// Pass post data to the page via props
return { props: { post } };
}
export default Post;
Fallback: true
如果fallback
为true
,则getStaticProps
的行为会发生变化,Next.js将在构建时将提供的路径渲染为HTML。当某个路径在构建时未生成时,将在用户请求页面时按需生成。
当你的应用程序有很多可以静态生成的路由,但你不想因为只在构建时生成一部分页面而增加构建时间时,这将非常有用。
触发页面生成的用户的浏览器会得到一个fallback HTML,这通常是一个带有加载状态的页面。这样做的原因是静态HTML可以从CDN提供,确保页面始终快速,即使它尚未生成。
按需静态生成额外页面的示例
import { useRouter } from 'next/router';
import fetch from 'node-fetch';
function Post({ post }) {
const router = useRouter();
// If the page is not yet generated, this will be displayed
// initially until getStaticProps() finishes running
if (router.isFallback) {
return <div>Loading...</div>;
}
// Render post...
}
// This function gets called at build time
export async function getStaticPaths() {
return {
// Only `/posts/1` and `/posts/2` are generated at build time
paths: [{ params: { id: 1 } }, { params: { id: 2 } }],
// Enable statically generating additional pages
// For example: `/posts/3`
fallback: true,
};
}
// This also gets called at build time
export async function getStaticProps({ params }) {
// params contains the post `id`.
// If the route is like /posts/1, then params.id is 1
const res = await fetch(`https://.../posts/${params.id}`);
const post = await res.json();
// Pass post data to the page via props
return { props: { post } };
}
export default Post;
要了解更多关于getStaticPaths
的信息,请参考数据获取文档。
getServerSideProps
如果你从一个页面导出一个名为getServerSideProps
的async
函数,Next.js将在每次请求时渲染此页面(SSR)。
getServerSideProps
始终在服务器端运行,并且代码会自动从浏览器捆绑包中剔除,从而确保发送到浏览器的代码更少。这样,你就不必担心在服务器和浏览器环境中执行数据获取代码,因为它们之间存在一些不一致性。在许多情况下,这会提高性能,因为服务器通常与数据源具有更快的连接。它还通过减少数据获取逻辑的暴露来提高安全性。
这允许您使用任何异步甚至同步数据获取技术,包括 fetch
、REST、GraphQL,甚至直接访问数据库。
当使用next/link
在页面之间导航时,而不是在浏览器中执行getServerSideProps
,Next.js会向服务器发出一个请求,该请求将返回调用getServerSideProps
的结果。
export async function getServerSideProps(context) {
return {
props: {}, // will be passed to the page component as props
};
}
context
参数是一个包含以下键的对象
params
:如果此页面使用动态路由,则params
包含路由参数。如果页面名称为[id].js
,则params
将类似于{ id: ... }
。要了解更多信息,请查看动态路由文档。req
:HTTP请求对象。res
:HTTP响应对象。query
:查询字符串。
以下是一个示例,它使用getServerSideProps
在请求时获取数据并渲染它
function Page({ data }) {
// Render data...
}
// This gets called on every request
export async function getServerSideProps() {
// Fetch data from external API
const res = await fetch(`https://.../data`);
const data = await res.json();
// Pass data to the page via props
return { props: { data } };
}
export default Page;
要了解更多关于getServerSideProps
的信息,请参考数据获取文档。
预览模式
如本文前面所述,静态生成在你的页面从无头CMS获取数据时非常有用。但是,当你在无头CMS上编写草稿并希望立即在页面上预览草稿时,它并不理想。由于输出是静态的,因此预览更改变得更加困难,因为你必须重新生成该静态页面。
Next.js中getStaticProps
的引入开辟了新的可能性,例如在某些条件下利用Next.js的按需渲染功能。
例如,当从你的无头CMS预览草稿时,你希望绕过静态渲染,并按需渲染包含草稿内容的页面,而不是已发布的内容。你希望Next.js仅在此特定情况下绕过静态生成。
我们很高兴宣布Next.js的一项新的内置功能来满足此需求:预览模式。
预览模式允许用户绕过静态生成的页面,以按需渲染(SSR)例如来自CMS的草稿页面。
但是,你不仅限于某些CMS系统。预览模式直接与getStaticProps
和getServerSideProps
集成,因此它可以与任何类型的数据获取解决方案一起使用。
使用next start
或通过部署到Vercel Edge Network时,预览模式已可用。
在https://next-preview.vercel.app/上自己试用预览模式。
与CMS提供商的合作
getStaticProps
允许你从任何数据源(包括CMS系统)获取数据
我们正在积极与CMS生态系统中的许多主要参与者合作,提供关于与Next.js集成的示例和指南。
当前正在积极开发的示例包括
如果您的公司活跃在 CMS 生态系统中,我们很乐意与您合作! 欢迎联系我们的团队 邮箱 或 Twitter。
内置 Sass 支持全局样式表
Next.js 9.2 引入了 对全局 CSS 样式表的内置支持,以更好的默认值替换 next-css
插件,从而提供更优化的结果。
发布后,我们越来越多地被要求集成 Sass 支持,因为许多迁移到 Next.js 的企业都拥有基于 Sass 的现有设计系统。
在调查 Next.js 插件使用情况时,我们发现大约 30% 的 Next.js 应用程序今天使用 next-sass
。相比之下,使用原生 CSS 的占 44%,使用 Less 的占 6%。
此外,next-sass
存在与 next-css
相同的缺失约束。这意味着您可以在项目的每个文件中导入 Sass 文件,但是,此导入的 Sass 文件将对整个应用程序全局生效。
在考虑这些统计数据和反馈后,我们很高兴地宣布 Next.js 现在内置支持导入 Sass 样式表。
要开始在您的应用程序中使用全局 Sass 导入,请安装 sass
npm install sass
然后,在 pages/_app.js
中导入 Sass 文件。
例如,考虑以下名为 styles.scss
位于项目根目录下的样式表
$primary-color: #333;
body {
padding: 20px 20px 60px;
margin: 0;
color: $primary-color;
}
如果不存在,则创建一个 pages/_app.js
文件。然后,导入 styles.scss
文件
import '../styles.scss';
// This default export is required in a new `pages/_app.js` file.
export default function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />;
}
由于样式表本质上是全局的,因此必须在 自定义 <App>
组件 中导入。这对于避免全局样式的类名和顺序冲突是必要的。
在开发过程中,以这种方式表达样式表允许您的样式在您编辑时自动更新到页面上。
在生产环境中,所有 Sass 和 CSS 文件将自动连接到单个最小化的 .css
文件中。此 CSS 文件将通过 <link>
标签加载,并自动注入到 Next.js 生成的默认 HTML 标记中。
此新功能完全向后兼容。如果您正在使用 @zeit/next-sass
或其他与 CSS 相关的插件,则禁用此功能以避免冲突。
如果您目前正在使用 @zeit/next-sass
,我们建议您从 next.config.js
和 package.json
中删除该插件,从而在升级后迁移到内置的 Sass 支持。
内置 Sass CSS 模块支持组件级样式
Next.js 现在支持使用 [name].module.scss
文件命名约定,使用 Sass 文件的 CSS 模块。
与之前在 Next.js 5+ 中使用 next-sass
可用的支持不同,全局 Sass 和 CSS 模块现在可以 **共存**——next-sass
要求应用程序中的所有 .scss
文件都作为全局或本地处理,但不能同时处理两者。
CSS 模块通过自动创建唯一的类名来本地作用域 Sass。这允许您在不同的文件中使用相同的 Sass 类名,而无需担心冲突。
此行为使 CSS 模块成为包含组件级 Sass 的理想方式。CSS 模块文件 **可以在应用程序中的任何位置导入**。
要开始在您的应用程序中使用 Sass CSS 模块,请确保您已安装 sass
npm install sass
现在,考虑 components/
文件夹中可重用的 Button
组件
首先,创建 components/Button.module.scss
并包含以下内容
/*
You do not need to worry about .error {} colliding with any other `.css` or
`.module.css` files!
*/
$color: white;
.error {
color: $color;
background-color: red;
}
然后,创建 components/Button.js
,导入并使用上述 CSS 文件
import styles from './Button.module.scss';
export function Button() {
return (
<button
type="button"
// Note how the "error" class is accessed as a property on the imported
// `styles` object.
className={styles.error}
>
Destroy
</button>
);
}
Sass 文件的 CSS 模块是一个可选功能,仅为扩展名为 .module.scss
的文件启用。常规的 <link>
样式表 和 全局 Sass 样式 仍然受支持。
在生产环境中,所有 CSS 模块文件都会自动连接到**多个最小化和代码分割的 .css
文件**中。这些 .css
文件表示应用程序中的热执行路径,确保为应用程序的绘制加载最少的 CSS。
与上面一样,此新功能完全向后兼容。如果您正在使用 @zeit/next-sass
或其他与 CSS 相关的插件,则禁用此功能以避免冲突。
如果您目前正在使用 @zeit/next-sass
,我们建议您从 next.config.js
和 package.json
中删除该插件,从而迁移到内置的 Sass 支持。
404 自动静态优化
Next.js 9 的发布引入了 自动静态优化 的概念,当页面没有阻塞数据需求时,Next.js 将在构建时自动生成页面为静态 HTML。但是,有一个页面没有自动渲染为静态 HTML:404 页面。404 页面未自动静态化的主要原因是,为 404 提供支持的 /_error
页面处理的不仅仅是 404,例如错误。
鉴于 404 页面会为不存在的路由渲染,按需渲染页面可能会导致成本增加和服务器负载增加。
我们通过两种方式帮助您取得成功
- 默认的 Next.js 体验会生成一个静态的 404 页面
- 在自定义 404 页面时,它仍然确保您最终得到一个静态页面
此功能完全向后兼容,因此,如果您目前拥有自定义的 pages/_error.js
,它将继续用于 404 页面,直到您添加 pages/404.js
。
默认情况下使用静态 404 页面
当您的应用程序没有自定义的pages/_error.js
页面时,Next.js 将自动静态生成 404 页面,并在需要提供 404 时使用该页面。这会自动发生,无需任何更改。
使用 pages/404.js 自定义 404 页面
要覆盖默认的 404 页面,您现在可以创建一个pages/404.js
文件,它仍然会在构建时自动进行静态优化。如果您的应用程序包含 404 页面,则此页面将用于代替pages/_error.js
来渲染 404。
export default () => <h1>This is the 404 page</h1>;
32+ kB 更小的运行时 (15 kB+ Gzip)
Next.js 支持与React本身相同的浏览器,无需任何配置。这包括 Internet Explorer 11 (IE11) 和所有流行的浏览器(Edge、Firefox、Chrome、Safari、Opera 等)。
作为此兼容性的一部分,我们还将您的应用程序编译为与 IE11 兼容:这允许您安全地使用ES6+ 语法特性、Async/Await、对象展开语法等等——所有这些都无需任何配置。
此编译过程的一部分还涉及透明地注入必要的特性 polyfill(例如Array.from
或Symbol
)。但是,这些 polyfill 仅在不到 10% 的网络流量中必要,在大多数情况下是为了支持 IE11。
从 Next.js 9.3 开始,Next.js 将自动加载支持旧版浏览器的 polyfill,并且仅在这些旧版浏览器中加载 polyfill。
在实践中,这意味着对于90%+ 的用户,您的首次加载大小将减少 32 kB 或更多。
对于依赖更多浏览器功能的更大应用程序,这些大小节省将更大。
此优化是完全自动的,无需任何应用程序更改即可利用它!
社区
我们非常高兴看到 Next.js 采用率的持续增长
- 我们拥有超过927位独立贡献者。
- 在 GitHub 上,该项目已被收藏超过46,600次。
- 该示例目录拥有超过226 个示例。
Next.js 社区现在拥有超过15,250名成员。社区现在可以在 GitHub 讨论区找到,这是一个供社区讨论和提问的新场所!加入我们!
我们感谢我们的社区以及所有帮助塑造本次发布的外部反馈和贡献。
特别感谢Jeff Escalante对新的数据获取方法提供的宝贵反馈。
非常感谢所有为本次发布做出贡献的人:@arcanis、@lgordey、@ijjk、@martpie、@jaywink、@fabianishere、@dijs、@TheRusskiy、@quinnturner、@timneutkens、@lfades、@vvo、@adithwip、@rafaelalmeidatk、@bmathews、@Spy-Seth、@EvgeniyKumachev、@chibicode、@piglovesyou、@HaNdTriX、@Timer、@janicklas-ralph、@devknoll、@prateekbh、@ethanryan、@MoOx、@rifaidev、@msweeneydev、@motiko 和 @balazsorban44 的帮助!