Next.js

作者: 贪恋冬天的幸福 | 来源:发表于2022-06-11 18:09 被阅读0次

    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请求向服务端,

    相关文章

      网友评论

          本文标题:Next.js

          本文链接:https://www.haomeiwen.com/subject/wjsbmrtx.html