next- react的ssr解决方案

238 阅读2分钟

SSR

目前市面上的C端网站大都全部已经向ssr看齐了,所以想要打造一个快速seo和fcp的web应用。ssr是作为我们前端人员必不可少的技能。ssr主要是以node环境返回的html页面,以实现运行的过程在服务端,这样就没必要把运行阶段的时间消耗在客服端了。

import express from "express";
import childProcess from "child_process";
import { renderToString } from "react-dom/server";
import Home from "@/pages/Home";

const app = express();
const content = renderToString(<Home />);

app.get("*", (req, res) => {
  res.send(`
    <html
      <body>
        <div>${content}</div>
      </body>
    </html>
  `);
});

app.listen(3000, () => {
  console.log("ssr-server listen on 3000");
});

childProcess.exec("start http://127.0.0.1:3000");

为什么要学习next?

市面上也出现了许多框架如nuxt和next分别是vue和react的服务端渲染的方案。为什么我要选择next而不是nuxt呢,主要目前为止不管是官方的重视程度还是社区的活跃度,在目前的市场环境里nuxt的成熟度还不如next。所以本次的分享主要是以next来学习ssr的解决方案。

什么是NextJs

首先查看NextJs官网给出了如下的解释(官网地址: nextjs.org):

The React Framework for the Web. Used by some of the world's largest companies, Next.js enables you to create high-quality web applications with the power of React components

总结就是用的公司多,框架水平NB,经住了很多项目的磨练。然后官网就给出了NextJs学习教程,做一个Dashboard网站。

个人觉得官网的解释还不是很有说服力,于是找了一些比较好的文章。

总结就是服务端渲染+完善的工程化机制

Next.js 可以带给我们什么?

Next.js 是一个 React web 应用框架,这是官方对自己的定义,然后它主要做的事情有以下几点:

  1. 完善的工程化机制
  2. 良好的开发和构建性能
  3. 智能文件路由系统
  4. 多种渲染模式来保证页面性能体验
  5. Suspense与Streaming

完善的工程化机制

你不需要自己去配置 webpack 方案,它已经内置了以下工程化基础:

  • babel 内置,支持JS代码向后兼容
  • postcss 内置,支持CSS代码向后兼容
  • browserslist 支持配置兼容的浏览器信息,配合 babelpostcss 工作。
  • TypeScript 可选择使用,保证代码的质量,以及可阅读性和可维护性。
  • eslint 可选择使用,检测代码格式,可自定义规则。vscode 编写代码,或者build打包时都会有提示。
  • prettier 可通过扩展使用,格式化代码,可自定义规则。
  • css modules 内置
  • css-in-js 可扩展使用
  • tailwind css 可扩展使用

多种渲染模式来保证页面性能体验

渲染模式是决定页面性能很重要的因素,也是 Next.js 最核心的一部分,如next可以帮助我们随时在组件里开启不同的渲染模式。

 Next.js 实现 CSR

以下都以app route为例子

// pages/csr.js
"use client"
import React, { useState, useEffect } from 'react'
 
export default function Page() {
  const [data, setData] = useState(null)
 
  useEffect(() => {
    const fetchData = async () => {
      const response = await fetch('https://jsonplaceholder.typicode.com/todos/1')
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`)
      }
      const result = await response.json()
      setData(result)
    }
 
    fetchData().catch((e) => {
      console.error('An error occurred while fetching the data: ', e)
    })
  }, [])
 
  return <p>{data ? `Your data: ${JSON.stringify(data)}` : 'Loading...'}</p>
}

 Next.js 实现 SSR

export default async function Page() {
  const data = await getData()
  return (
    <>
      <h1>图片 ID:{data[0].id}</h1>
      <img src={data[0].url} width="300" />
      <CatDetail />
    </>
  )
}

Suspense与Streaming

1、Suspense

传统ssr的缺点,传统的ssr渲染是线性的、阻塞的,传统ssr的渲染步骤如下:

  1. 服务端获取所有数据
  2. 服务端渲染 HTML
  3. 将页面的 HTML、CSS、JavaScript 发送到客户端
  4. 使用 HTML 和 CSS 生成不可交互的用户界面(non-interactive UI)
  5. React 对用户界面进行水合(hydrate),使其可交互(interactive UI)

这意味着服务端只能在获取所有数据后渲染 HTML,React 只能在下载了所有组件代码后才能进行水合

为了解决这些问题,React 18 引入了  组件。我们来介绍下这个组件:

<Suspense> 允许你推迟渲染某些内容,直到满足某些条件(例如数据加载完毕) 你可以将动态组件包装在 Suspense 中,然后向其传递一个 fallback UI,以便在动态组件加载时显示。如果数据请求缓慢,使用 Suspense 流式渲染该组件,不会影响页面其他部分的渲染,更不会阻塞整个页面。

    // app/dashborard/page.js
import { Suspense } from 'react'

const sleep = ms => new Promise(r => setTimeout(r, ms));

async function PostFeed() {
  await sleep(2000)
  return <h1>Hello PostFeed</h1>
}


async function Weather() {
  await sleep(8000)
  return <h1>Hello Weather</h1>
}

async function Recommend() {
  await sleep(5000)
  return <h1>Hello Recommend</h1>
}

export default function Dashboard() {
  return (
    <section style={{padding: '20px'}}>
      <Suspense fallback={<p>Loading PostFeed Component</p>}>
        <PostFeed />
      </Suspense>
      <Suspense fallback={<p>Loading Weather Component</p>}>
        <Weather />
      </Suspense>
      <Suspense fallback={<p>Loading Recommend Component</p>}>
        <Recommend />
      </Suspense>
    </section>
  )
}

Streaming

介绍

Suspense 背后的这种技术称之为 Streaming。将页面的 HTML 拆分成多个 chunks,然后逐步将这些块从服务端发送到客户端。 image.png

智能文件路由系统

next智能化的配置了一系列的文件的路有导航系统,我们可以先从next的目录结构看起:

image.png