🏞快速理解 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.jsonbrowser 字段:{ ... "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