BSR、SSG、SSR

Next.js 的三种渲染方式(BSR、SSG、SSR)【转载】https://zhuanlan.zhihu.com/p/341229054

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 来过度

2. 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 数据我们只在服务器获取了,但又是怎样传递给前端的呢?

发现玄机

[图片上传失败...(image-74b22b-1664503300290)]

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

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

静态化的时机

环境

  1. 开发环境 ,每次请求都会运行一次 getStaticProps 这是为了方便你修改代码重新运行
  2. 生成环境,getStaticProps 只在 build 是运行一次,这样可以提供一份 HTML 给所有的用户下载

如何体验生成环境

yarn build
yarn start

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

[图片上传失败...(image-dbbf0e-1664503300289)]

解读

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

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

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

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

三种文件类型

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

[图片上传失败...(image-cf752-1664503300289)]

  • 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>你的浏览器是 {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问题

可以生成用户相关的内容

三种渲染模式如何选择

[图片上传失败...(image-83ad20-1664503300289)]

  • 有动态内容吗?没有什么也不用做,自动渲染为 HTML
  • 有动态内容,动态内容和客户端相关吗?相关就只能用客户端渲染 BSR
  • 有动态内容,动态内容跟请求/用户相关吗?相关就只能用服务端渲染 SSR 或 BSR
  • 其他情况可以用 SSG 或 SSR 或 BSR

转载至: https://zhuanlan.zhihu.com/p/341229054

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,547评论 4 374
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,787评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,175评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,752评论 0 223
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,169评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,056评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,195评论 2 321
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,980评论 0 214
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,724评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,872评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,344评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,667评论 3 264
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,379评论 3 245
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,202评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,992评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,189评论 2 286
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,987评论 2 279