NextJs 页面预渲染方案

1.1 预渲染介绍

  • 原因:

    1. 浏览器访问网站的流程是首先对React组件做初始化渲染,此是页面并没有展示接口数据,只是对初始的state数据进行渲染。然后通过 useEffect 从服务器获取数据,并更新 state,state 因为发生改变,react 使组件函数重新 render 一遍,页面因此重新渲染,因此页面实际上执行了两次渲染流程
    2. 而 SEO 搜索引擎是在第一次渲染组件之后就开始抓取页面信息的,此页面数据是空的,无法抓取,那么网站被搜索到的概率就大大降低。为了解决SEO的问题,使用预渲染页面,在第一个 render 流程之后就已经将接口数据渲染到组件中,完成页面的展示,就不需要再进行二次请求渲染了
    3. 要使得预渲染的页面包含数据,NextJS 提供了两种形式的预渲染(pre-rendering):
      • static site generation 静态生成页面 SSG
      • server-side rendering 服务端渲染页面 SSR
  • 不使用预渲染时的页面表现:

    • 创建一个带有请求的普通组件:

      import { useEffect, useState } from "react";
      export default function Home() {const [items, setItems] = useState([]);useEffect(() => {fetch("url").then((resp) => resp.json()).then((data) => {setItems(data);});}, []);return (<div className="Home">{items.map((item) => {return (<h4 style={{ margin: "15px 0" }} key={item.qa_id}>{item.title}</h4>);})}</div>);
      }
      
    • 页面加载以后,右键 - 网页源代码 查看页面生成情况:
    • 说明组件中的ajax数据仍然是通过js后续动态插入到页面中的

1.2 SSG 静态生成页面

1. 出现原因

  • 假如有一个新闻资讯页面,每个用户访问这个页面,看到的新闻内容都是一样的

    • 传统的React做法是先首次初始化渲染组件内容,然后再次发送ajax请求获取数据,将获取到的数据再一次渲染到页面中,用户想要看到这个页面需要经过两次渲染,且每一个用户都要经历这样的过程;那么这个过程效率非常低
    • 如果html页面和数据直接在后端整合好,那么用户每次访问页面就可以直接看到页面的完整内容,不需要再进行二次请求加载,这样的话,页面的加载速度更快了,也更加有利于SEO的搜索,页面白屏时间减少,从而加速页面加载速度,这个过程就是动态内容静态化,也就是今天的主题:SSG

2. 生成不带数据的静态页面

  • 默认情况下,Next.js 使用 “静态生成” 来预渲染页面但不涉及获取数据

    function About() {return <div>About</div>
    }
    export default About
    
  • 请注意,此页面在预渲染时不需要获取任何外部数据。在这种情况下,Next.js 只需在构建时为每个页面生成一个 HTML 文件即可。如 .next/server/pages 目录所示:

3. 生成需要获取数据的静态页面

  • 某些页面需要获取外部数据以进行预渲染。有两种情况,在每种情况下,你都可以使用 Next.js 所提供的以下函数:

    • 您的页面 内容 取决于外部ajax数据:使用 getStaticProps
    • 你的页面 paths(路径) 取决于外部数据:使用 getStaticPaths (通常还要同时使用 getStaticProps)

情况一:页面内容取决于外部ajax数据

  • 写一个Home组件,并发送ajax请求获取数据,再将数据渲染到Home组件中:

    function Home({ items, time }) {return (<div className="Home"><p>timer: {time}</p>{items.map((item) => {return (<h4 style={{ margin: "15px 0" }} key={item.qa_id}>{item.title}</h4>);})}</div>);
    }export async function getStaticProps() {// 在这里调用外部接口,可以写各种服务端代码:连数据库,访问文件系统.....const resp = await fetch("https://m.lanqb.com/api/home/heat/qa-list");const items = await resp.json();// 此函数必须返回 { props: { 需要携带的数据 } } 对象,Home 组件在构建时将接收到 `items` 参数。return {props: {items,time: Date.now(), // 为了方便对比yarn dev和yarn build的区别,加入时间戳},};
    }export default Home;
  • 查看右键网络源代码:

    • 可以看到,源代码中直接包含ajax的数据了
  • 查看网络请求:

    • 浏览器的网络监听器中也不再有数据接口的请求了
getStaticProps 函数说明:
  • 在开发环境下(yarn dev),每次请求都会运行一次getStaticProps,因为这是为了方便我们修改代码重新运行

    • 每次刷新页面,发现时间戳都会变化
  • 在生产环境(yarn start),getStaticProps仅在构建时**(next build/yarn build)**运行一次,然后提供一份HTML给所有用户下载
    • 代码经yarn build构建之后,通过yarn start启动生产服务器,刷新页面,发现时间戳保持不变。意味着页面刷新不再执行getStaticProps
  • getStaticProps 只能在page 组件中使用
  • getStaticProps 只运行在服务器端,可以访问文件系统,可以连数据库。写在这个函数中的代码在build阶段就执行过了,所以这些代码永远不会到达客户端

情况二:组件使用动态路径参数作为外部数据

  • Next.js 允许你创建具有 动态路由 的页面。例如,你可以创建一个名为 pages/home/detail/[id].js 的文件用以展示该 id 对应的博客文章详情信息。当你访问 home/detail/1 路径时将展示 id: 1 的博客文章详情数据。但是,在构建 id 所对应的内容时需要从外部获取动态id
  • 为了解决这个问题,Next.js 允许你从动态页面(在这里是 pages/home/detail/[id].js)中 export(导出) 一个名为 getStaticPaths 的 async(异步) 函数。该函数在构建时被调用,并允许你指定要预渲染的所有页面路径(也就是要预渲染的所有数据id对应的详情页面)
动态路由参数实现静态预渲染的实现步骤
  1. 创建 pages/home/detail/[id].js 文件,并实现 getStaticPaths 函数:

    function Detail({ detailData }) {// 获取文章详情信息数据渲染到组件中。return <div className="Detail">{JSON.stringify(detailData)}</div>;
    }export default Detail;export async function getStaticPaths() {// 调用api,获取文章列表const resp = await fetch("url");const items = await resp.json();// 遍历文章列表,获取文章id,根据文章id生成所有需要预渲染的静态页面路径const paths = items.map((item) => ({params: { id: `${item.qa_id}` }, // id的值必须是字符串类型,因为动态路径参数也是字符串类型,要保持一致}));// { fallback: false } 不在上述预渲染列表范围内的路径id,显示404页面。return { paths, fallback: false };
    }export async function getStaticProps({ params }) {const resp = await fetch(`url/${params.id}`);const detailData = await resp.json();return {props: {detailData,},};
    }

getStaticPaths 函数说明

  • 仅在构建时运行。它非常适合预渲染需要在构建时使用动态数据的类似路径(如/blog/:id)。当与 getStaticProps 结合使用时,它会从 数据库/接口 等数据结构中生成动态页面,然后 Next 可以对其进行静态服务
  • getStaticPaths 只能和 getStaticProps 配合使用,不能和 getServerSideProps 一起使用

fallback 配置说明

  • fallback:false ,只有在构建时生成静态页面,且只针对 getStaticPaths 返回的路径进行生成(可在 .next/server/pages 目录下查看构建的静态页面),超出路径的页面则进入404页面,因此如果你后期新增加了一篇文章,在不重新构建部署的情况下直接访问是不可行的
  • fallback:true, 在构建时没有生成的路径不会导致404页面,可以先暂时为用户提供一个临时页面,同时 next 会去后台会运行 getStaticProps 函数,对构建时没有生成的路径页面进行动态预渲染。临时页面需要在页面组件中添加,如果不处理则会程序报错。处理过程如下:
    import { useRouter } from "next/router";
    function Detail({ detailData }) {const router = useRouter();if (router.isFallback) {return <div>正在请求,请稍后...</div>; // 临时页面,给用户友好提示}return <div className="Detail">{JSON.stringify(detailData)}</div>;
    }export default Detail;
    
    • getStaticProps 预渲染完成后,浏览器会收到生成路径的 JSON,页面将从临时页面切换到有数据的预渲染页面
    • 同时,Next.js 将此路径添加到预渲染页面列表中(可以在build之后,查看 .next/server/pages 目录,有生成的动态预渲染页面文件)。以后用户对同一路径的后续请求将直接返回预渲染页面,就像在构建时预渲染的其他页面一样,不再展示临时页面

fallback为true的场景

  • 如果应用程序有大量依赖数据的静态页面,部分页面构建时预渲染,部分页面请求时预渲染。
  • 当有人请求尚未生成的静态页面时,用户将看到带有加载指示器的页面。 不久之后,getStaticProps 完成,页面将使用请求的数据呈现。 之后每个请求相同页面的人都将获得静态预渲染的页面
  • 这确保用户始终获得快速体验,同时保留快速构建和静态生成的优势

4. 什么时情况下使用静态生成

  • 建议尽可能使用静态生成 (带有或不带数据),因为你的所有 page(页面)都可以只构建一次并托管到服务器上,这比让服务器根据每个页面请求来渲染页面快得多

  • 还可以对多种静态类型的页面使用“静态生成”,包括:

    • 营销页面
    • 博客文章和个人简历
    • 电商产品列表
    • 帮助和文档

5. 无法使用静态生成页面的解决方案

  • 如果你无法在用户请求之前预渲染页面(比如用户触发触底加载更多,分页列表页面),则“静态生成” 不是 一个好主意。这也许是因为你的页面需要显示频繁更新的数据,并且页面内容会随着每个请求而变化

  • 在这种情况下,您可以执行以下任一操作:

    • 静态生成客户端渲染 一起使用:你可以跳过页面某些部分的预渲染,然后使用客户端 JavaScript 来填充它们
    • 使用 服务器端渲染: Next.js 针对每个页面的请求进行预渲染。由于 CDN 无法缓存该页面,因此页面加载速度会变慢,但是保证了预渲染的页面将始终是最新的页面

6. 静态生成的执行时机

  • HTML 在 构建时 生成,并在每次页面请求(request)时重用
  • 构建时间发生在您构建用于生产的应用程序 (next build/yarn build) 时。运行时发生在应用在生产中运行时 (next start/yarn start)
  • .next/server/pages 作为页面缓存池,静态生成的页面和数据都保存在这里,包含 ISR 更新后的页面

1.3 增量静态重新生成 (ISR)

SSG 提供一种方式可以让页面在一定频率下进行更新,称为 Incremental Static Regeneration (ISR) ,在SSG的基础上更新页面,而无需重新构建整个网站。增量静态重新生成 (ISR) 使我们能够在每个页面的基础上使用静态生成,使用 ISR,咱们可以在扩展到数百万页的同时保留静态的优势。

export async function getStaticProps() {const resp = await fetch("url");const items = await resp.json();return {props: {},revalidate: 10, // 第一个请求后,10s后再请求重新生成静态页面};}

1.4 总结

  • next 为每个页面静态生成html和json,无论是SSG和SSR,只是生成页面的时机有所不同。
  • html 结构:
    <html>
    <head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link rel="preload" href="/_next/static/css/a2265cb7a0c708a7.css" as="style"/><link rel="stylesheet" href="/_next/static/css/a2265cb7a0c708a7.css" data-n-g=""/><noscript data-n-css=""></noscript><!-- polyfillFiles --><script defer="" nomodule="" src="/_next/static/chunks/polyfills-5cd94c89d3acac5f.js"></script><!-- 公共js  --><script src="/_next/static/chunks/webpack-69bfa6990bb9e155.js" defer=""></script><script src="/_next/static/chunks/framework-6e4ba497ae0c8a3f.js" defer=""></script><script src="/_next/static/chunks/main-83803dd478f5b5bc.js" defer=""></script><script src="/_next/static/chunks/pages/_app-73483fad2904193b.js" defer=""></script><!-- 该页面所需 js --><script src="/_next/static/chunks/pages/users/%5Bid%5D-dc83b756f9b806d1.js" defer=""></script><!-- lowPriorityFiles --><script src="/_next/static/NVp1g76ArQSS85zjw1gWB/_buildManifest.js" defer=""></script><script src="/_next/static/NVp1g76ArQSS85zjw1gWB/_ssgManifest.js" defer=""></script><script src="/_next/static/NVp1g76ArQSS85zjw1gWB/_middlewareManifest.js" defer=""></script>
    </head>
    <body><!-- 预渲染出的页面内容 --><div id="__next" data-reactroot=""><div>...</div></div><!-- 当前页面组件接收到的 props 等 --><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"user":{...}},"__N_SSG":true},"page":"/users/[id]","query":{"id":"1"},"buildId":"NVp1g76ArQSS85zjw1gWB","isFallback":false,"gsp":true,"scriptLoader":[]}</script>
    </body>
    </html>
    
  • json数据结构:
    {"pageProps": {"user": {...}},"__N_SSG": true
    }
    
  • 当前页面组件显示所需的props数据,由 getStaticProps 返回,切换页面时拿到该json文件进行页面内容的动态替换

nextjs系列教程(四):页面预渲染-SSG相关推荐

  1. 史上最详细的Android Studio系列教程四--Gradle基础

    史上最详细的Android Studio系列教程四--Gradle基础 转载于:https://www.cnblogs.com/zhujiabin/p/5125917.html

  2. docker 打包镜像_Spring Boot2 系列教程(四十一)部署 Spring Boot 到远程 Docker 容器

    不知道各位小伙伴在生产环境都是怎么部署 Spring Boot 的,打成 jar 直接一键运行?打成 war 扔到 Tomcat 容器中运行?不过据松哥了解,容器化部署应该是目前的主流方案. 不同于传 ...

  3. asp.NET自定义服务器控件内部细节系列教程四

    如大家要转载,请保留本人的版权: /* *Description:asp.NET自定义服务器控件内部细节系列教程 *Auther:崇崇-天真的好蓝 *MSN:chongchong2008@msn.co ...

  4. ASP .NET Core Web MVC系列教程四:添加模型

    系列文章目录:ASP .NET Core Web MVC系列教程:使用ASP .NET Core创建MVC Web应用程序 上一个教程:ASP .NET Core Web MVC系列教程三:添加视图 ...

  5. ASP .NET Core Web Razor Pages系列教程四:使用数据库进行交互 entity-framework(MySQL/MariaDB 版)

    系列文章目录:系列教程:使用ASP.NET Core创建Razor Pages Web应用程序 - zhangpeterx的博客 系列教程代码的GitHub地址:ASP .Net Core Razor ...

  6. ASP .NET Core Web Razor Pages系列教程四:使用数据库进行交互(SqlServer 版)

    系列文章目录:系列教程:使用ASP.NET Core创建Razor Pages Web应用程序 - zhangpeterx的博客 上一个教程:ASP .NET Core 系列教程三:自动生成Razor ...

  7. ClickHouse系列教程四:允许远程连接 allow remote access

    ClickHouse系列教程: ClickHouse系列教程 先查看ClickHouse server端监听端口的状态: root@ubuntu:/var/lib/clickhouse/# lsof ...

  8. ASP.NET 5系列教程 (四):向视图中添加服务和发布应用到公有云

    向视图中添加服务 现在,ASP.NET MVC 6 支持注入类到视图中,和VC类不同的是,对类是公开的.非嵌套或非抽象并没有限制.在这个例子中,我们创建了一个简单的类,用于统计代办事件.已完成事件和平 ...

  9. Redis系列教程(四):Redis为什么是单线程、及高并发快的3大原因详解

    Redis的高并发和快速原因 1.redis是基于内存的,内存的读写速度非常快: 2.redis是单线程的,省去了很多上下文切换线程的时间: 3.redis使用多路复用技术,可以处理并发的连接.非阻塞 ...

最新文章

  1. 第一次,人类在人工神经网络中发现了“真”神经元
  2. android 显示 PDF 文件
  3. Linux System Programming --Chapter Eight
  4. SAP物料主数据采购视图采购价值代码设置
  5. mmap直接操作底层,相当于驱动
  6. python识别图像数字诊断模块_opencv+python 机读卡识别
  7. 如何获得带时间的ping的结果
  8. python多级索引修改
  9. java多台_Java 多态
  10. row_number() over()排序功能
  11. Java面试题:程序计数器为什么是私有的?
  12. [十二省联考2019]希望
  13. [cf] Deltix Round, Autumn 2021 A. Divide and Multiply
  14. mysqld: [ERROR] Found option without preceding group in config file D:\MySql\MyS ql\my.ini at line 1
  15. tiny6410无法使用usb下载功能的解决办法
  16. 基于java(springboot框架)的购物商城系统 毕业论文
  17. nmn对血管作用怎样,nmn产品哪个牌子好,补充NMN可恢复血管的僵硬度!
  18. 达摩院特别版-视觉AI训练营Day1——学习笔记
  19. 记Mininet安装
  20. 2020机械员-通用基础(机械员)证考试及机械员-通用基础(机械员)模拟考试题库

热门文章

  1. 苹果11怎么录屏_苹果7怎么录屏?这篇干货教程给你答案
  2. python 导入 包,模块,类,函数
  3. 微信多开设置网络代理服务器,还在玩微信双开吗?你out了,哥教你三开四开无数开...
  4. Hugging Face Transformers Agent
  5. 水泥工厂 3D 可视化大屏,全工艺流程数字孪生,破局产线低效运转!
  6. 第四次实验 第一期 紫金学院 Java 抽象类和接口
  7. 第四次实验 第三期 紫金学院 Java 抽象类和接口
  8. 一篇文章搞定大数据安装(Hadoop、zookeeper、Spark、HBase、Hive)———附带详细步
  9. 妹子,你这是要风干它吗?
  10. FX3U PLC V10.0源代码及设计图。