BSR、SSG、SSR之next.js

Next.js 是一个轻量级的 React 服务端渲染框架

它支持三种渲染方式包括

客户端渲染 BSR (Broswer Side Render)

静态页面生成 SSG (Static Site Generation)

服务端渲染 SSR (Server Side Render)

旧瓶装新酒

上面说的几种渲染方式,其实并非什么新东西,其实可以和这些技术对应起来

BSR - 用 JS、Vue、React 创建 HTML

SSG - 页面静态化,把 PHP 提前渲染成 HTML

SSR - PHP、Python、Ruby、Java 后台的基本功能

不同点

Next.js 的预渲染可以与前端 React 无缝对接

下面,以一个文章列表页面作为例子,分别解析一下三种渲染方式吧

客户端渲染

客户端渲染,顾名思义就是只在浏览器上执行的渲染,通过Vue 和 React 构建的单页面应用SPA 都是采用这种方式渲染

缺点

1 .白屏,在 AJAX 得到渲染之前,页面中并没有内容,只能通过 Loading 来过度

  1. SEO 不友好,因为搜索引擎访问页面, 默认不会执行 JS,只能看到 HTML,而不会等待 AJAX 异步请求数据,所以搜索不到页面内容

代码

import {NextPage} from 'next';
import axios from 'axios';
import {useEffect, useState} from "react";
import * as React from "react";

type Post = {  id: string,  id: string,  title: string
}
const PostsIndex: NextPage = () => {  // [] 表示只在第一次渲染的时候请求
    const [posts, setPosts] = useState<Post[]>([]);  
    const [isLoading, setIsLoading] = useState(false);  
    useEffect(() => {      
      setIsLoading(true);      // 使用 AJAX 异步请求数据
      axios.get('/api/posts').then(
        response => {        
          setPosts(response.data);        
          setIsLoading(false);      
        }, 
        () => {   setIsLoading(true);  
      })  
    }, []);  
    return (<div>
      <h1>文章列表</h1> 
      {isLoading ? <div>加载中</div> : posts.map(p => <div key={p.id}> {p.id}</div>)}
      </div> )
};
export default PostsIndex;

当网络不好的时候,loading 的时间很长,页面肯能会出现长时间白屏

由于初次请求的 HTML 中并没有文章内容,需要通过 AJAX 异步加载数据,而这个加载数据渲染的过程都是在客户端完成的,所以称为客户端渲染


静态页面生成 SSG

在文章列表页面里,其实每个用户查到的内容都是一样的

那为什么还需要在每个人的浏览器上渲染一遍呢?

为什么不在后端渲染好,然后发给每个人

这样就可以

N 次渲染变成了 1 次渲染

N 次客户端渲染变成了 1 次静态页面生成

这个过程成为 动态内容静态化

优缺点

优点:这种方式可以解决白屏问题、SEO 问题

缺点:所有用户看到的都是同一个页面,无法生成用户相关内容

如何实现

首先我们来想一个问题

该如何获取 posts 呢? 因为加载数据的操作在后端,想通过 AJAX 获取 posts 显然不合适

答案是: 通过 getStaticProps 获取 posts

getStaticProps 是 Next.js 提供的一个方法,会在后端执行,返回一个 props,NextPage 在渲染的时候可以使用这个 props

代码

import {GetStaticProps, NextPage} from 'next';
import {getPosts} from '../../lib/posts';
import Link from 'next/link';
import * as React from 'react';

type Post = {id: string,title: string
}

type Props = {posts: Post[];
}
// props 中有下面导出的数据 posts
const PostsIndex: NextPage<Props> = (props) => {const {posts} = props;
 // 前后端控制台都能打印 -> 同构
  console.log(posts);
  return (  
    <div>
      <h1>文章列表</h1>    
      {posts.map(p => <div key={p.id}>
      <Link href={`/posts/${p.id}`}> 
      <a> {p.id}</a>
      </Link>    
      </div>)
      }  
    </div>
  );
};

export default PostsIndex;
// 实现SSG
export const getStaticProps: GetStaticProps = async () => {
  const posts = await getPosts();
  return {  
    props: {    
      posts: JSON.parse(JSON.stringify(posts))  
    }
  };
};

前端是怎么不通过 AJAX 获取到数据的

posts 数据我们只在服务器获取了,但又是怎样传递给前端的呢?

发现玄机

我们可以看到玄机就藏在 id 为 NEXT_DATA_ 的 script 标签中,里面储存了传给前端的 props 数据

这就是同构 SSR 的好处,后端可以将数据直接传给前端,而不需要 AJAX 异步获取

静态化的时机

环境

在 开发环境 ,每次请求都会运行一次 getStaticProps 这是为了方便你修改代码重新运行

在 生成环境,getStaticProps 只在 build 是运行一次,这样可以提供一份 HTML 给所有的用户下载

如何体验生成环境

yarn build
yarn start

打包后我们可以会看到这样

解读

我看看到的页面前的三种图标,分别是 λ ○ ●

λ (Serve) SSR 不能自动创建 HTML (下面会介绍)

○ (Static) 自动创建 HTML (发现你没用到 props)

● (SSG) 自动创建 HTML + JSON (等你用到 props)

三种文件类型

build 完成后,我们查看.next 文件里面,发现 posts.html、posts.js、posts.json

posts.html 含有静态内容,用于用户直接访问

post.js 也含有静态内容,用于快速导航(与 HTML 对应)

posts.json 含有数据,跟 posts.js 结合得到页面

为什么不直接把数据放入 posts.js 呢?

显然是为了 posts.js 接受不同的数据,当我们展示每篇博客的时候,他们的样式相同,内容不同,就会用到这个功能

动态内容静态化

如果动态内容与用户无关,那么可以提前静态化

通过 getStaticProps 可以获取数据

静态内容+数据(本地获取) 就得到了完整的页面

代替了之前的 静态内容+动态内容(AJAX 获取)


服务端渲染(SSR)

如果页面和用户相关呢?

这种情况较难提前静态化,需要在 用户请求时,获取用户信息,然后 通过用户信息去数据库拿数据,如果非要做,就要给每个用户创建一个页面,有时候这些数据更新极快,无法提前静态化, 比如微博首页的信息流

那怎么办?

要么客户端渲染, 会出现白屏

要么服务端渲染 SSR,没有白屏

运行时机

无论时开发环境还是生成环境,都是在请求之后运行 getServerSideProps

代码

和 SSG 代码基本一致,不过使用了 getSeverSideProps

这段代码实现的时,服务器响应请求后获取浏览器信息,返回给前端展示

import {GetServerSideProps, NextPage} from 'next';
import * as React from 'react';
import {IncomingHttpHeaders} from 'http';

type Props = {browser: string
}
const index: NextPage<Props> = (props) => {
  return (  <div>    
    <h1>你的[浏览器](https://www.w3cdoc.com)是 {props.browser}</h1>  
  </div>);
};
export default index;

export const getServerSideProps: GetServerSideProps = async (context) => {
  const headers:IncomingHttpHeaders = context.req.headers;
  const browser = headers['user-agent'];
  return {  
    props: {    browser  }
  };
};

SSR 原理

推荐 在后端 renderToString() 在前端 hydrate()

后端将页面渲染,返回 HTML String 格式,传递到前端前端进行 hydrate() ,会保留 HTML 并附上时间监听,也就是说后端渲染 HTML,前端添加监听。

前端也会渲染一次,用以确保前后端渲染结果一致


总结

客户端渲染 SSR

只在浏览器上运行,缺点 SEO 不友好,白屏

静态页面生成 SSG

Static Site Generation,解决白屏问题、SEO 问题

缺点:无法生成和用户相关的内容 (所有用户请求的结果都一样)

服务端渲染 (SSR)

解决白屏问题、SEO问题

可以生成用户相关的内容

三种渲染模式如何选择

有动态内容吗?没有什么也不用做,自动渲染为 HTML

有动态内容,动态内容和客户端相关吗?相关就只能用客户端渲染 BSR

有动态内容,动态内容跟请求/用户相关吗?相关就只能用服务端渲染 SSR 或 BSR

其他情况可以用 SSG 或 SSR 或 BSR



请遵守《互联网环境法规》文明发言,欢迎讨论问题
扫码反馈

扫一扫,反馈当前页面

咨询反馈
扫码关注
返回顶部