Next.js目录
一、前言:二、预渲染:1、静态网站生成(SSG)2、静态增量生成(ISR)3、服务端渲染 (SSR) 三、预渲染的好处:四、客户端渲染(CSR)1、静态 HTML 导出
一、前言:
最近在做 Next
项目的时候使用 Next.js
的预渲染 API
时踩了一些坑,各种数据获取和传入的 API
看的眼花缭乱,它们具体的作用和表现对我来说是一个黑盒。因此,我认真的看了 Next
. js
的官方文档中渲染部分的介绍并结合自己的实践进行了总结。
在 Next.js
中想要使用某种渲染方式,例如我想指定 SSR
或是 SSG
,并不是说直接在某个配置文件中改一个配置就可以的,而是我们在页面中使用了一些相关的 API
,而 Next.js
在打包时检测到了才会进行相应的页面构建策略。
如果你对于 Next.js
或其他预渲染框架中各种渲染机制一知半解,那么这篇文章肯定能对你有所启发。
二、预渲染:
Next.js
是一个基于 React
的服务端渲染框架,其主要的特点是支持预渲染。默认情况, Next.js
会预渲染每个页面。这意味着 Next.js
会将网站的所有页面提前生成静态 HTML
文件并保存下来。当用户访问网站时,服务器会返回预渲染好的静态 HTML
文件,而不是使用 JavaScript
动态生成网页内容。
在直接使用 Vue
或者 React
时,页面的渲染是完全由 JS
执行的,在 JS
未渲染完成前,页面中可能只有类似 <div id='Page'></div>
的一个容器标签,当 JavaScript
文件被加载并执行时,它们会根据代码逻辑和数据来生成最终的 HTML
标记和页面内容,并将其插入到页面中的容器标签中。
Next.js
的预渲染又分为两种形式,分别是 静态网站生成 SSG
( Static
Site
Generation
) 和 服务端渲染 SSR
( Server
Site
Rendering
)。这两者有何区别呢?
SSG
:在构建打包产物时( build
time
)生成 HTML
,它会在每一次请求中重复使用。SSR
: HTML
是在每一次请求时生成的。 Next.js
官方更加推荐使用 SSG
,因为 SSG
静态生成的页面可以由 CDN
缓存,无需额外配置,相比之下性能更好。但是在有些场景中你不得不使用 SSR
,例如一些需要 实时更新 和 动态生成内容 的应用程序,例如社交媒体应用、在线游戏等。这些应用会提供动态内容和实时交互功能,因此必须在服务器端动态生成页面。
关于 SSG
和 SSR
的选择最终是取决于 你的页面能不能在用户发送请求前就展示出来?
1、静态网站生成(SSG)
Next.js
中静态网站生成一般分为三种情况,分别是
内容
依赖外部数据页面的 路径
依赖外部数据 不需要依赖外部数据:
function About() { return <div>About</div>}export default About
这种情况最简单,Next.js
会在打包时直接生成一个静态的 HTML
。
页面的内容依赖外部数据:
export default function Blog({ posts }) { // 渲染文章...}// 这个函数会在 build 时接收请求export async function getStaticProps() { // 请求 API 获取文章数据 const res = await fetch('https://.../posts') const posts = await res.json() // 在构建时,Blog 组件可以接收到 posts 这个 props return { props: { posts, }, }}
为了能够在预渲染的时候拿到外部的数据,我们可以在定义组件的那个文件中暴露一个异步函数 getStaticProps
,在打包页面时 Next.js
会先执行 getStaticProps
中的操作,例如发送请求,数据处理之类的。
然后我们可以返回一个对象,其中 props
字段的值会在渲染组件时作为 props
传入,这样就实现了构建时从外部获取数据。
页面的路径依赖外部数据:
Next.js
的路由是由文件系统驱动的,每个页面都对应着一个文件,这个文件的路径就是这个页面的路由路径。例如, pages
/ index
. js
对应着根路径 /,而 pages
/ about
. js
对应着 / about
路径。
当然不是所有的页面都是固定路由的,而动态路由就是一种可以基于 URL
参数动态生成页面的路由。例如,你可以创建一个 / posts
/[ id
] 的页面路由,用于显示具有不同 id
值的文章详情页面。
而如果我们使用了动态路由,例如前面提到的文章 id
,那么 Next.js
如何得到我们有哪些文章 id
以实现预渲染呢?
// 这个函数在 build 时会调用export async function getStaticPaths() { // 请求 API 获取数据 const res = await fetch('https://.../posts') const posts = await res.json() // 基于 posts 获取我们想要预渲染的路径 const paths = posts.map((post) => ({ params: { id: post.id }, })) // 在 build 时我们只会预渲染 paths 数组中的路径 // { fallback: false } 意为其他的路由都会返回 404 return { paths, fallback: false }}
我们可以在定义组件的那个文件中再暴露一个异步函数 getStaticPaths
,这个函数可以返回一个包含 paths
和 fallback
属性的对象, paths
是一个包含了所有动态路由路径的数组,它决定了哪些路径将被预渲染。而 fallback
则表示访问还没有生成 html
的路径时,要采取怎么样的策略,它存在三种情况:
false
:访问任何在 getStaticPaths
() 函数中未返回的路径都会响应 404 404 404 页面。true
: build
时未生成的路径不会返回 404 404 404 页面。而是在第一次请求时进行 SSR
并返回生成的 HTML
。这个生成的 HTML
会被缓存,也就是说你访问这个路径时,即便对应的数据已经更新了还是会返回原本的页面,直到缓存过期重新生成 HTML
。blocking
: blocking
策略与设置为 true
时相同,不同点在于设置为 true
时,我们会直接进入访问的路径,并同时执行
getStaticProps
,并且可以通过 router
isFallback
判断 getStaticProps
是否已经执行完成,而 blocking
策略则会先执行 getStaticProps
,直到执行完成才会进入新的页面。也就是同步与异步的关系 虽然预渲染这么方便,将我的每个页面都生成静态文件了,那如果页面更新了怎么办呢?难道我需要执行 build
重新生成所有页面嘛?这里就需要引入 ISR
的概念了。
2、静态增量生成(ISR)
ISR
( Incremental
Static
Regeneration
) 是一种在静态站点生成过程中,增量更新部分页面的技术。传统的静态网站生成方式需要每次完全重新生成全部页面,这在网站内容变化频繁的情况下可能会导致生成时间较长,同时也可能会降低网站的性能。
ISR
的思想是只 重新生成需要更新部分页面。这样可以显著减少生成时间和资源占用,并且能够提高网站的响应速度。当用户请求一个需要更新的页面时, ISR
会在后台自动重新生成该页面,然后将其缓存,以便下一次请求时可以快速响应。
要在 Next
. js
中开启 ISR
,只需要在前面介绍的 getStaticProps
函数中返回一个 revalidate
属性,下面放一个官方的示例:
function Blog({ posts }) { return ( <ul> {posts.map((post) => ( <li key={post.id}>{post.title}</li> ))} </ul> )}// 这个方法会在服务端渲染或者 build 时被调用// 当使用了 serverless 函数、开启 revalidate 并且接受到新的请求时也会被重新调用export async function getStaticProps() { const res = await fetch('https://.../posts') const posts = await res.json() return { props: { posts, }, // Next 将会尝试重新生成页面: // - 接受到新的请求 // - 每隔最多十秒钟 revalidate: 10, // 单位为秒 }}// 这个方法会在服务端渲染或者 build 时被调用// 当使用了 serverless 函数该路径还没有被生成过就会被重新调用export async function getStaticPaths() { const res = await fetch('https://.../posts') const posts = await res.json() // 基于 posts 获取我们想要预渲染的路径 const paths = posts.map((post) => ({ params: { id: post.id }, })) // 在 build 时,我们将只预渲染 paths 中的路径 // { fallback: 'blocking' } 在页面不存在时按需进行服务端渲染。 return { paths, fallback: 'blocking' }}export default Blog
ISR
的实现方式是将某些页面标记为可 ISR
页面。这些页面在生成时与 SSG
页面相同,但它们还有一个“ revalidate
”(再生成时间) 。这个时间告诉 Next.js
何时需要重新生成页面。
例如,假设我们需要构建一个新闻网站,希望能在首页上显示最新的新闻文章。在 Next.js
中,我们就可以将首页标记为 ISR
页面,并设置重新生成时间为 10 10 10 秒钟。这意味着,Next.js
将在访问首页时,返回上一次生成的静态页面,同时在后台开始重新生成新的页面。 10 10 10 秒后,当新页面生成完毕后,Next.js
会将新页面替换旧页面,并提供给用户。
所以关于增量生成我们也可以简单的总结为:“传统的预渲染如果需要更新内容就得将全部页面重新生成 HTML
,而 增量生成 允许我们单独设置某一些页面重新生成“,这样就能节省很多不必要的性能开支了。
3、服务端渲染 (SSR)
在 Next.js
中,如果启用了 SSR
,那么每次的每次请求都会重新生成页面。想要开启 SSR
,我们可以在定义组件的那个文件中暴露一个异步函数 getServerSideProps
,这个方法类似于 getStaticProps
,只是执行的时机不同, getServerSideProps
会在每次页面接受请求时都调用。
根据 getServerSideProps
的调用时机,我们可以知道这个方法适用于展示需要经常更新的数据。下面放个官方例子:
export default function Page({ data }) { // 渲染数据...}// 这个方法每次请求时都会调用export async function getServerSideProps() { // 从外部 API 获取数据 const res = await fetch(`https://.../data`) const data = await res.json() // 通过 props 向组件内传入数据 return { props: { data } }}
除了调用时机外, getServerSideProps
与 getStaticProps
并无二致,因此还是很好理解的。
三、预渲染的好处:
预渲染的目的是可以带来更好的性能以及 SEO
。
HTML
文件可以在 CDN
中缓存。体验好:首次加载页面时可以直接返回 HTML
,再加载 JS
文件,白屏时间更短。 提升 SEO
:更多的可读性的 HTML
内容可以提高网站的可索引性和可识别性。相比之下,传统的方式是使用 JavaScript
动态生成网页内容,而爬虫并不总是能够正确地读取和理解 JavaScript
。 四、客户端渲染(CSR)
Next.js
的构建产物是需要启动一个服务才能访问的,而如果我们有特殊需求,不想要让前端单独跑一个服务,而是直接打包成一个静态文件,直接访问指定的 url
去获取资源,那么你就可以使用 Next.js
的 客户端渲染 + next export
去实现。
在 Next.js
中想要使用客户端渲染也很简单,只要上述的这些 API
,例如 getStaticProps
、 getServerSideProps
… 我们都没有使用,数据都是通过在组件内部使用 axios
或者 fetch
去发送请求获取并渲染的,那么我们使用的就是纯客户端渲染了,与直接使用 Vue
或 React
并无二致。
但即便你的每一个页面都是使用的客户端渲染,在执行 build
时,Next.js
还是会为你打包成一个需要持续运行的 Node.js
服务,而不是直接打包为一个静态文件。
想要导出为静态文件,需要使用单独的打包方式,那就是静态 HTML
导出。
1、静态 HTML 导出
Next.js
可用于生成静态应用程序,可以实现在浏览器中使用 React
而无需 Node.js
服务器。就可以使用 next export
命令去将你的站点构建为一个静态的文件。
首先更新 next.config.js
中的 output
为 export
:
/** * @type {import('next').NextConfig} */const nextConfig = { output: 'export',}module.exports = nextConfig
然后更新 package.json
中 build
指令以包含 next export
:
"scripts": { "build": "next build && next export"}
运行 npm run build
会生成一个 out
目录。
这个功能是有很多限制的,一旦你启动了以下 Next.js
的特性,你就无法打包为纯静态产物:
Image Optimization (default loader)
Internationalized Routing
API Routes
Rewrites
Redirects
Headers
Middleware
Incremental Static Regeneration
getStaticPaths with fallback: true
getStaticPaths with fallback: 'blocking'
getServerSideProps
以上这些特性都得依赖 Node.js
服务,因此就无法打包为纯静态产物,具体可以参考文档:nextjs.org/docs/advanc …
在实际的项目开发中,我们的 Next.js
应用往往是同时采用多种渲染方式共同构建的,在合适的页面使用合适的策略,各种 API
结合使用,在保障功能可以正常实现的前提尽可能的去提升性能,使得用户体验更加流畅才是我们的根本目的。
如果这篇文章对你有所帮助~ 不妨点个? 支持下作者。