Next.js与React服务端渲染

最近在一个资讯类的项目中用了 Next.js 服务端渲染,体验了一把服务端渲染的速度,首屏直出,渲染速度666。

服务端渲染

在之前前后端没分离的时候,前端简单写一下 HTML 模版,后端通过诸如 Php、Java 等各种模版引擎把静态页面处理成动态模版渲染出来,那个时候就是服务端渲染了。但这样好麻烦,历史车轮滚滚向前,单页面应用的时代,后端只提供接口,不再负责模板的处理。再后来为了解决 SEO 的问题,顺带首屏渲染的问题,总不能再回到前后端不分离的年代吧,所以就是前端er自己选择的路,跪着也要走下,Vue 的 Nuxt.js,React 的 Next.js 等 SSR 框架应运而生。

我们平常广泛使用 Vue、React 在客户端渲染,服务端返回了一个空的 HTML 模版,然后内部加载JS,生成并操作 Dom,最后由浏览器渲染出页面,这样一系列的动作下来首屏加载会显的慢了不少。另外由于是个空的页面,爬虫无法识别,不利于 SEO,在浏览器中右键查看源码,可以看到页面是个空的 HTML。

Next.js与React服务端渲染_第1张图片

服务端渲染(SSR),服务端直接返回了 HTML,浏览器显示即可,无需等待 JavaScript 完成下载且执行才显示内容,不仅渲染速度大大加快,更利于搜索引擎的爬取,右键查看源码可以看到密密麻麻的 HTML 标签。

Next.js与React服务端渲染_第2张图片

优点

  • 更快的首屏加载速度
  • 更友好的 SEO

缺点

  • 增加了维护成本
  • 项目部署比单页面应用复杂

Next.js 基本使用

路由系统

pages 目录路由

  • Next.js 的路由系统基于文件路径自动映射,pages 目录内的文件会被自动处理成路由,所以通常 pages 下我们只放置页面路由的文件,其它组件不要放到 pages 目录下面。比如在 pages 目录下面创建了 login.js 和 register.js,那么路由就对应 /login 和 /register。
  • 同样,多级路由也是类似的处理。例如在 pages 下创建了 user 目录,user 下创建 login 和 register 文件。/user/login 对应 login 文件,/user/register 对应 register 文件。
    Next.js 的路由系统使我们不用再去关心路由,合理在 pages 建立目录。

动态路由

  • Next.js 也支持支持动态路由,在文件前携带 [param],例如 pages/post/[pid].js,会匹配到海报详情页面 /post/pid。
  • 预定义的 API 路由优先于动态 API 路由

    • pages/post/create.js, 将匹配 /post/create
    • pages/post/[pid].js`,将匹配 /post/1,但不匹配 /post/create

路由跳转与传参

next 提供了两种方式,分别是导航式路由 next/link 和 编程式 next/router

  1. Link

    href 为必须属性,可传递对象

    
    
    
  2. 编程式导航 next/router

    和 react hooks 中的 useHistory 用法一样

    import { useRouter } from 'next/router'
    const router = useRouter()
    //: 1
    router.push(`/article/${c.queueId}`)
    //: 2
    router.push({
    pathname: '/publish',
        query: {
          contentId: c.contentId,
          status: active
        }
    })

    路由参数获取

    Next.js 只能通过 query 来传递参数,不能使用 params。
    useRouter 或 getServerSideProps 方法内都可以拿到 query 参数

import { useRouter } from 'next/router'
const { query } = useRouter()
query.cid //: 获取 cid 参数

这种动态路由的参数通过 query 可以获取到,在 getServerSideProps 方法内也可以通过 params 获取
router.push(`/article/${c.queueId}`)

css

Next.js 支持 Css Module 和 Css-in-JS 这两种方式,二者自带样式隔离。

动态导入

Next.js 同样支持和 React 客户端一样的 ES2020 import() 语法来实现导入,在 React 单页面项目里面,Webpack 解析到该语法时会自动进行代码分割。在 Next.js 里面, 还可以使用next/dynamic 来动态导入组件,它们将在客户端懒加载。通过动态导入,对于一些不需要在服务端渲染的组件可以使用 dynamic 来处理。

const BreadCrumb = dynamic(() => import('@/components/ui/BreadCrumb'))

服务端请求 getServerSideProps

export async function getServerSideProps(context) {
  return {
    props: {}, // will be passed to the page component as props
  }
}
  • getServerSideProps,主要用于在服务端请求数据,比如我们列表的的首屏数据,像列表下一页的数据我们则可以放到客户端去获取。context 参数里面包含路由参数等对象。
  • 由于 getServerSideProps 是在服务端进行的请求,所以相关的 log 信息在终端才能看到,Network 面板里面是看不到请求情况包括 console 信息。
  • getServerSideProps 返回数据后,Next.js 会把这些数据写到 HTML源 码里面,即 window.__NEXT_DATA__.props,这样就实现了把数据传送到客户端,客户端有了这些数据,比如可以拿着 window.__NEXT_DATA__.props里面的数据初始化 React 组件的 props 等。在 console 里输入 __NEXT_DATA__,你就能看到 Next.js 在页面中封装了什么数据。

其它

  • 全面兼容最新 React 17版本
  • 链接跳转使用 Link,避免使用编程式导航,有利于 SEO。
  • Next.js 自带 Image 组件,自动优化图像,极大改善用户体验。
  • 可以自定义 document 页面 404页面、500页面等。当 Next.js 捕获到错误时,不论是接口错误还是代码运行时的错误,Next.js 服务内部会统一转化并抛出500异常。
  • 官方文档

服务端渲染常见问题

最后呢总结一下 Next.js 使用中遇到的问题,欢迎各路侠客补充。

useEffect 的注意点

服务端渲染时拿不到屏幕、元素宽高等尺寸信息,不要让元素的显示依赖于 useEffect 里面的设备宽高、 dom 位置计算。

useEffect(() => {
    //:错误示例
    //:服务端渲染的时候拿不到设备宽度deviceWidth 所以isDesktop的值不会改变
    deviceWidth > 960 && (isDesktop = true)
, [])

return (
    <>
      {isDesktop ? (
        
pc
) : null} )

一个响应式的页面,pc 端显示 HeaderBar 组件,m 端不显示,如何处理?

在客户端渲染的情况下,判断一下设备类型即可。但是在服务端渲染,在哪里判断设备类型呢?useEffect?不能在里面判断。

方案有二:

  1. 采用客户端渲染的处理方式。使用 next/dynamic 动态导入 HeaderBar 组件,这样 HeaderBar 组件将在客户端进行渲染,其它元素依然还是在服务端进行渲染。
  2. 依然采用服务端渲染的处理方式。通过 css 的媒体查询,在 m 端对 HeaderBar 进行 display:none,这也是一种处理办法。

你可能感兴趣的:(Next.js与React服务端渲染)