🏞快速理解 Next.js 中数据获取方式的作用及区别(getInitialProps、getServerSideProps、getStaticProps)
前言
Next.js 中一共有以下四种数据获取的方式,我们按照顺序逐一介绍:
- getInitialProps
- getServerSideProps
- getStaticPaths 与 getStaticProps
getInitialProps
getInitialProps
能够在页面中进行服务器端渲染,并允许你进行初始数据填充,这意味着在发送页面时已经在服务端中填充了数据。这对于 SEO 特别有用。
getInitialProps
的使用方式如下:
function Page({ stars }) {
return <div>Next stars: {stars}</div>
}
Page.getInitialProps = async (ctx) => {
const res = await fetch('https://api.github.com/repos/vercel/next.js')
const json = await res.json()
return { stars: json.stargazers_count }
}
export default Page
分析一下上面的代码,我们在页面组件中添加 getInitialProps
属性,属性值为一个异步函数,我们可以在函数中进行数据获取的操作,然后函数的返回值将获取到的作为 props 传入页面组件中。
在页面组件中使用了 getInitialProps
,也就说明当前的页面时开启的 SSR 的渲染方式。
从 getInitialProps
返回的数据在服务端渲染时会被序列化,类似于JSON.stringify
的做法。确保从 getInitialProps
返回的对象是一个纯粹的 Object,而不是使用 Date
、Map
或 Set
。
执行时机
getInitialProps
的执行时机分为两种情况,都是在运行时执行的:
- 直接访问页面时,
getInitialProps
会在服务端执行 - 通过
next/link
或next/router
跳转至某个页面时,getInitialProps
会在客户端执行。
注意点
1. getInitialProps
使用的限制
getInitialProps
只能在页面组件中使用,在其他子组件中是不能使用的。
2. getInitialProps
中模块的引入
如果你在 getInitialProps
中使用服务端的模块,请确保正确导入这些模块,否则会降低应用性能。
例如我们在 getInitialProps
引入了 faker
这个库进行一些模拟数据生成的操作,faker
这个库实际上是只想在服务端进行引入的,但是在打包时 webpack 识别到了引入就会将其一块打包在客户端的包中,这肯定是不必要的。因此我们可以使用 arunoda.me/blog/ssr-an… 这篇文章中介绍的三种方法进行处理:
-
使用
eval
函数:Webpack 无法静态分析 eval 中的内容。所以它不会打包
faker
模块。const faker = eval("require('faker')")
-
使用 Webpack 的
ignore
插件:ignore
插件可以用于在 Webpack 打包时忽略某些模块,使用方式如下:module.exports = { webpack: function (config) { config.plugins.push( new require('webpack').IgnorePlugin(/faker/) ) return config } }
-
使用
package.json
browser 字段:{ ... "browser": { "faker": false } ... }
-
3. getInitialProps 目前已基本被 getServerSideProps 替代
Next.js 在 9.3版本中引入了getServerSideProps
这个 API。前面提到了 getInitialProps
会根据情况在服务端或者客户端执行,如果我在 getInitialProps
进行了一些 node.js 或者操作了数据库这种纯服务端的操作,当 getInitialProps
在客户端执行时就会报错,除非自己去做兼容处理。
由于执行环境不统一, 使用成本较高,因此在 getServerSideProps
引入后, getInitialProps
就不再被推荐使用了。
接下来我们就讲讲 getServerSideProps
这个 API。
getServerSideProps
如果你从一个页面导出一个名为
getServerSideProps
(服务端渲染)的函数,Next.js 将在每次请求时使用getServerSideProps
返回的数据对这个页面进行预渲染。
getServerSideProps
的使用方式如下:
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
使用方式与 getInitialProps
几乎相同,都是一个用于请求数据的异步函数,将请求到的数据作为函数返回值,数据从页面的 props 中传入。
在页面组件中使用了 getServerSideProps
,也就说明当前的页面时开启的 SSR 的渲染方式。
执行时机
getServerSideProps
只会在服务器端运行,从不在客户端上运行。如果页面中使用 getServerSideProps
API,则:
- 当我们直接请求此页面时,
getServerSideProps
在请求时运行,并且此页面将使用返回的 props 进行预渲染 - 当我们通过在客户端使用
next/link
或next/router
进行页面跳转请求此页面时,Next.js 会向服务端发送 API 请求,服务端会执行getServerSideProps
。
注意点
1. getServerSideProps
的使用限制
- 与
getInitialProps
一样,getServerSideProps
只能在页面组件中使用,在其他子组件中是不能使用的。 - 必须直接导出
getServerSideProps
为一个独立的函数,而不是像getInitialProps
可以添加为页面组件的属性。
对比
- getServerSideProps 和 getInitialProps 都只能在页面组件中使用,而不能在子组件中使用
- getServerSideProps 和 getInitialProps 都可以在服务端执行
getStaticProps 与 getStaticPath
getStaticProps
是 Next.js 中非常重要的一个 API ,当在页面组件中使用了 getStaticProps
,说明当前的页面时开启了 SSG 的渲染方式。SSG 能是页面静态化进行渲染,除非重新进行编译或是设置 revalidate
实现 ISR 渲染。
getStaticProps
使用方式如下:
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,
},
}
}
使用方式和前面介绍的两个 API 基本一致,这里不赘述,主要讲讲 getStaticPaths
,getStaticPaths
使用方式如下:
/ 这个函数在 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
必须与 getStaticProps
共同使用,这个函数可以返回一个包含 paths
和 fallback
属性的对象,paths
是一个包含了所有动态路由路径的数组,它决定了哪些路径将被预渲染。而 fallback
则是回退的策略,它存在三种情况
false
:访问任何在getStaticPaths()
函数中未返回的路径都会响应 404 页面。true
:build 时未生成的路径不会返回 404 页面。而是在第一次请求时进行 SSR 并返回生成的 HTML。这个生成的 HTML 会被缓存,也就是说你访问这个路径时,即便对应的数据已经更新了还是会返回原本的页面,直到缓存过期重新生成 HTML。'blocking'
:'blocking'
策略与设置为true
时相同,不同点在于设置为true
时,我们会直接进入访问的路径,并同时执行getStaticProps
,并且可以通过router.isFallback
判断getStaticProps
是否已经执行完成,而'blocking'
策略则会先执行getStaticProps
,直到执行完成才会进入新的页面。也就是同步与异步的关系。
getStaticProps
可以加快渲染速度以及提升 SEO ,但如果我们的页面时经常改变的,相对比较动态的,那使用 getStaticProps
进行静态渲染就不太合适。反而应该使用 getServerSideProps
,但 getServerSideProps
又会在每次请求时都执行,因此性能会差一些,用户访问的速度会更慢,但相比纯客户端渲染来说还是有 SEO 上的优势的。
运行时机
getStaticProps
始终在服务端上运行,从不在客户端上运行,具体的运行时机如下:
- 执行
next build
时总会执行 - 设置
fallback: true
,访问页面后,会在页面渲染时同步在后台运行 - 设置
fallback: blocking
,在初次渲染时先运行,执行结束后渲染页面 - 当使用了
revalidate
时会在后台运行 - 当使用了
revalidate()
函数时会在后台按需运行
既然 getStaticProps
只会在服务端运行,那就代表我们可以在这里执行一些数据库操作或是引入一些 Node.js 的模块,这里不用担心依赖的处理,Next.js 会自动为我们将 getStaticProps
中引入的模块排除在 Webpack 的打包范围内的。
getStaticPaths
则只会在生产构建期间运行,不会在运行时调用。
注意点
1. getStaticProps
与 getStaticPaths
的使用限制
getStaticProps
与 getStaticPaths
都只能从 页面组件 导出,不能 将其从非页面文件、_app
、_document
或 _error
中导出。
2. 开发环境中的 getStaticProps
在开发环境(next dev)中,getStaticProps
将在每次请求时调用。
3. 函数处理的限制
在 getStaticProps
中是无法访问传入请求的,因为请求在传入之前我们就需要生成静态 HTML,并将 HTML 返回,也就是说 getStaticProps
是在请求进入前执行的。
总结
这篇文章介绍了 Next.js 中官方提供的所有数据获取方式,这些数据获取方式看似相似,其实在理解了对应的渲染机制后还是比较好理解的。掌握了这些 API 就能理解 Next.js 与传统 CSR 渲染之间的区别了。
如果文章对你有帮助,除了收藏外不妨点个赞支持下作者,respect!
转载自:https://juejin.cn/post/7214831158873571383