Next.js

getStaticProps
技术细节:

  • 构建时运行(only runs at build time)
    因为getStaticProps在构建时运行,所以它不能接收只能在请求时可用的数据,例如查询参数或者 HTTP 请求头,来生成静态页面。
  • 直接写服务端运行代码(write server-side code directly)
    注意getStaticProps只能运行在服务端,它永远不会运行在客户端。它也不会被浏览器当作JS模块使用。这意味着你可以写类似于查询数据库之类的代码,而不用担心它们被发送到浏览器。你不应该发起一个API路由请求在getStaticProps里面,取而代之的是,你可以直接在getStaticProps中写服务端代码。
  • 静态生成HTML和JSON(statically generates both HTML and JSON)
    当一个带有 getStaticProps静态方法的页面在构建时预渲染的时候,除了页面的 HTML 文件,Next.js同时生成了一个保存执行getStaticProps后的结果的JSON文件。
    这个 JSON 文件将被在客户端通过路由导航渲染的时候使用。当你导航至一个使用getStaticProps预渲染的页面的时候,Next.js 读取这个JSON文件(在构建时已完成计算: pre-computed at build time)然后使用它作为页面组件的props。这意味着客户端渲染过渡(transitions)将不会调用getStaticProps方法,而只有导出的 JSON 数据被使用了。
    当使用增量式静态生成(Incremental Static Generation)时getStaticProps将被执行out of band,以生成被客户端导航所需的JSON。你可能会看到相同的页面有多次请求,然而这是可预期的(intended)并且对终端用户体验没有影响的。
  • 只在页面中被允许(only allowed in a page)
    getStaticProps只能从一个页面中导出。你不能从一个非页面的文件导出它。这个限定的其中一个原因是 React 在页面渲染之前需要有全部的数据。同样地,你必须使用export async function getStaticProps() {}-如果你添加getStaticProps作为一个页面组件得属性,是不会起作用的。
  • 在开发环境每次请求都会执行
    在开发环境(next dev),getStaticProps将会在每次请求的时候被执行。
  • 预览模式
    在一些示例中,你可能暂时想要避开静态生成,在请求时渲染页面而不是构建时。例如,你可能在使用一个无头的内容管理系统(headless CMS),然后想在发布它之前预览草稿。
    这个使用场景是被 Next.js 的一个特点叫做 预览模式 得以实现的。
    getStaticPaths
    当一个页面有动态路由的时候,使用 getStaticProps 它需要定义一个路径列表用来在构建时加载到HTML。
    如果你在一个使用动态路由的页面导出一个 getStaticPaths
    异步(async)方法,Next.js 将静态地预渲染所有由 getStaticPaths 指定的路径。
export async function getStaticPaths() {
  return {
    paths: [
       { params: { ... } }
    ],
    fallback: true,   //false or 'blocking' 
  } 
}
  1. paths 属性是必须的
    paths 属性定义了哪些路径会被预渲染。例如,如果你有一个使用动态路由的页面 pages/posts/[id].js,如果你在这个页面中导出 getStaticPaths方法并且返回如下的 paths
return {
  paths: [
     { params: { id: '1' } },
     { params: { id: '2' } }
  ],
  fallback: ...
}

然后 Next.js 会在构建时使用这个页面组件静态生成 posts/1posts/2
注意 params 的值必须与页面中使用的参数对应:

  • 如果页面名称是 pages/posts/[postId]/[commentId],那么params 应该包含 postIdcommentId
  • 如果页面名称是 pages/[...slug],那么params应该包含 slug,并且是一个数组。例如,如果这个数组是 ['foo', 'bar'],那么 Next.js 将会静态生成页面 /foo/bar
  • 如果页面使用可选的路由,支持null[]undefinedfalse 渲染根路由。例如,如果你配置 slug: falsepages/[[...slug]],Next.js 将静态生成页面 /
  1. fallback 属性是必须的
    fallback: false
    如果 fallbackfalse,那么任何路径都不会生成并且变成一个404页面。你可以在你仅有少量的路径去预渲染的时候这样做,这样的话在构建时都是静态页面。当并不经常添加新的页面的时候这样做很有用。但是当你需要然后新的时候的时候,你就需要重新构建。
    这里有一个预渲染一个博客详情页面 pages/posts/[id].js 的示例,博客列表会从一个内容管理系统(CMS)中获取并且通过 getStaticPaths 返回。然后,每个详情页面,它通过 getStaticProps 从一个内容管理系统(CMS)获取数据。这里示例如下:
// pages/posts/[id].js

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) => ({
       params: { id: 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
如果fallbacktrue,那么getStaticProps的行为会有如下变化:

  • getStaticPaths 获取的路径会在构建时调用 getStaticProps 方法渲染成 HTML 文件;
  • 在构建时未生成的路径不会以 404 页面返回。相反,当请求不存在的页面路径时,Next.js 会渲染一个当前页面的 回退( fallback) 版本。注意,回退(fallback) 版本不会提供给像谷歌这样的爬虫程序,而是以阻塞模式呈现路径。
  • 在后台,Next.js 会根据请求路经执行 getStaticProps 方法静态生成页面的HTML和JSON。
  • 当这些都完成之后,浏览器接收 JSON 数据根据对应的生成路径。这些数据会自动在页面渲染的时候被使用。从用户的角度来看,页面将从备用页面切换到完整页面。
  • 在同时,Next.js 将路经添加到预渲染页面列表中。对同一路径的后续请求将渲染已经生成的页面,就像构建时预渲染的其他页面一样。

注意:fallback: true 在使用 next export 时是不被支持的。

  1. 回退页面(fallback pages)
    在页面的回退版本:
  • 页面的 props 将是空的。
  • 使用 router,你可以发现如果回退版本被渲染的话,router.isFallback 值会变成true
    这里有一个使用 isFallback 的示例:
//pages/posts/[id].js
import { useRouter } from 'next/router';

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 },
         //Re-generate the post at most once per second
         //if a request comes in
         revalidate: 1,
     }
}

export default Post;
  1. fallback: true 何时最有用?
    当你的应用有大量的依赖于数据(depend on data)的静态页面 fallback: true ,你想要预渲染所有的商品页面,但这样你的构建将花费很长时间。取而代之的是,你可以静态生成一个很小的页面集合,其余部分通过使用 fallback: true 生成。当有用户请求一个还没有生成的页面时,用户会看到页面中有一个加载指示器。很快的,getStaticProps 执行完成,页面会根据请求到的数据渲染。之后同样请求此页面的任何用户将会得到静态预渲染的页面。
    这确保了用户在保持快速构建和静态生成的好处的同时始终拥有最快的体验。
    fallback: true 并不会更新已经生成的页面,具体可查看增量静态再生(Incremental Static Regeneration)。

  2. fallback: 'blocking'
    如果 fallbackblockinggetStaticPaths未返回的新路径将等待HTML完全生成,与 SSR 是完全相同的,然后被缓存下来以供将来的请求使用,因此每个路径只发生一次。
    getStaticProps会有如下行为:

  • getStaticPaths 返回的路径将在构建时执行 getStaticProps 渲染成HTML。
  • 在构建时未生成的路径不会返回一个 404 页面,而是,Next.js 会在第一次请求时执行 SSR 然后返回生成的HTML。
  • 当这些执行完之后,浏览器接收生成路径的HTML。从用户的角度来看,它将从”浏览器正在请求页面“转换未”加载完整页面“。没有加载/回退状态闪烁。
  • 与此同时,Next.js 将路径添加到预渲染页面的列表。对后续的同一路径的请求将呈现已经生成的页面,就像其他在构建时已经预渲染的页面一样。
    fallback: 'blocking' 默认不会更新已经生成的页面。需要更新已经生成的页面,可以结合使用静态增量再生(Incremental Static Regeneration)和fallback: 'blocking'

当使用 next export 的时候 fallback: 'blocking' 是不支持的。

  1. 什么时候使用 getStaticPaths
    如果要静态预渲染使用动态路由的页面,则应使用getStaticPath。
  2. 使用 TypeScript:GetStaticPaths
    需要使用 TypeScript,可以引入 next 的 GetStaticPaths 类型;示例如下:
import { GetStaticPaths } from 'next'

export const getStaticPaths: GetStaticPaths = async () => {
  //...
}

技术细节:

  1. 结合 getStaicProps使用
    当你的页面中 getStaticProps 使用动态路由参数的时候,你必须使用 getStaticPaths。你不能结合 getServerSideProps 使用 getStaticPaths
  2. 只在构建时在服务端运行
  3. 只允许在页面中被使用
    getStaticPaths 只能被一个页面导出,不能在非页面文件导出。
    同时,你必须使用 export async function getStaticPaths() {} ,如果你将 getStaticPaths 设置为一个页面组件的属性将不会生效。
  4. 开发环境每次请求都会运行
    在开发环境(next dev),getStaticPaths 将会在每次请求时都执行。

getServerSideProps( 服务端渲染 Server-side Rendering)

当你从一个页面导出一个叫做 getServerSidePropsasync 函数 ,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: ... },要了解更多,可以查看 Dynamic Routing documentation
  • req:HTTP 请求消息体。
  • res:HTTP 响应体。
  • query:查询对象。
  • preview:当页面以预览模式(preview mode)请求时,preview 值为 true,否则为 false
  • previewData:值为通过 setPreviewData 设置的预览数据。
  • resolvedUrl:请求URL的规范化版本,为客户端转换去掉 next/data 前缀,并包含原始查询值。
  • locale:包含当前活跃的路由(如果已启用国际化路由)。
  • locales:包含所有支持的路由(如果已启用国际化路由)。
  • defaultLocale:包含配置的默认路由(如果以启用国际化路由)。
    getServerSideProps 应该返回一个对象包含以下属性:
  • props:一个可选的对象,该对象会传递给页面组件。值应该是一个序列化对象或者一个返回序列化对象的 Promise
  • notFound:一个可选的 Boolean 值,定义一个页面是否要返回 404 状态。
    下面是其工作原理的示例:
export async function getServerSideProps(context) {
    const res = await fetch(`https://...`);
    const data = await res.json()

    if (!data) {
        return {
             notFound: true,
         }
    }
    
     return {
         props: {}, // will be passed to the page component as props
     }
}
  • redirect:一个可选的重定向值,此值允许重定向到一个内部的和外部的资源。它应该形如 { destination: string, permanent: boolean }。在一些稀有的示例中,你可能需要为较旧的HTTP客户端分配自定义状态代码,以便正确重定向。在这些情况下,可以使用 statusCode 属性而不是 permanent 属性,但不能同时使用这两个属性。下面是它的工作原理示例:
export aysnc function getServerSideProps(context) {
    const res = await fetch(`https://.../data`);
    const data = await res.json()
    
     if (!data) {
          return {
               redirect: {
                    destination: '/',
                    permanent: false,
               },
           }
      }
   
       return {
              props: {},  //will be passed to the page component as props
        }
}

注意:你可以导入顶级作用域中的模块,以便在 getServerSideProps 中使用。 getServerSideProps 中使用的导入不会为客户端绑定。这意味着你可以直接在 getServerSideProps 中编写服务器端代码。这包括从读写文件系统或数据库。
注意:你不应该在 getServerSideProps 中使用 fetch() 调用API路由。相反,直接导入API路由中使用的逻辑。对于这种方法,您可能需要稍微重构代码。从外部API获取是很棒的实践。

  1. 提供 req 中间件
    传递给 getServerSideProps 的上下文中的 req 提供了解析传入请求 (req)的内置中间件。该中间件是:
  • req.cookies:包含请求中 cookies 的对象,默认是 {}
  1. 使用示例
    下面是一个使用 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
  1. 何时应该使用?
    只有当需要预渲染一个必须在请求时获取数据的页面的时候使用 getServerSideProps 。第一个字节到达的时间(TTFB)将会比 getStaticProps 慢,因为服务端需要在每次请求时计算结果,并且如果没有额外的配置,CDN无法缓存结果。
    如果你不需要预渲染数据,你应该考虑在客户端请求数据。客户端获取数据参考这里
  2. 使用 TypeScript: GetServerSideProps
    需要使用类型定义(TypeScript),可以从next中引入GetServerSideProps,示例如下:
import { GetServerSideProps } from 'next'

export const getServerSideProps: GetServerSideProps = async (context) => {
      //...
}

如果你想为页面的 props 获取推断类型,可以使用 InferGetServerSidePropsType<typeof getServerSideProps>,示例如下:

import { InferGetServerSidePropsType } from 'next'

type Data = { ... }

export const getServerSideProps = async () => {
    const res = await fetch('https://.../data')
    const data: Data = await res.json()
    
    return {
       props: {
           data,
       }
    }
}

function Page({ data }: InferGetServerSidePropsType<typeof getServerSideProps>) {
    //will resolve posts to type Data
}

export default Page

技术细节:

  • 只在服务端运行
    getServerSideProps 只在服务端运行,永远不会在浏览器端运行。如果一个页面使用 getServerSideProps,然后:
    1. 当你直接请求这个页面,getServerSideProps 会直接在请求时运行,页面将会根据返回的数据进行预渲染。
    2. 当你在客户端请求通过 next/link or next/router 请求,Next.js 发送一个API请求向服务端,
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,348评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,122评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,936评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,427评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,467评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,785评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,931评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,696评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,141评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,483评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,625评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,291评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,892评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,324评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,492评论 2 348

推荐阅读更多精彩内容