上次写了一个 SSRDEMO,今天写个小 Demo 来从头实现一下 reactSSG,来理解下 SSG 是如何实现的。

什么是 SSG

SSGStatic Site Generation 静态站点生成,是指将在构建时就提前生成静态 HTML 页面,速度很快,一般用于以下场景:

  1. SEO (搜索引擎优化):由于部分搜索引擎对 CSR 内容支持不佳,所以 SSG 可以提升网站在搜索引擎结果中的排名。
  2. 静态站点:比如博客、CMS 系统输出站点等,由于内容以静态内容居多,都可以使用 SSG

如何实现

简单的 SSGSSR 实现原理差不多,只是时机不同: SSR 渲染 HTML 的过程在服务端,而 SSG 则在构建时。渲染同样是通过在 Node 端导入要渲染的组件,然后调用 react-dom/server 包中提供的 renderToString 方法将该组件的渲染内容输出为 HTML 保存。

系统设计

下面使用 SSG 实现一个简单的静态博客系统:

  • 每篇博文都使用一个 JSON 文件来记录其中的信息(为了简化没有使用 markdown,可以理解为 markdown 博文信息已提前转换为 JSON 文件)
  • 构建时会将博文编译为静态 HTML 文件,并生成一个博文列表的 HTML 文件
  • 通过静态文件服务器启动即可展示博文

组件设计

可以看出我们只需要两个组件:

  • 一个 Post 组件,用于接收博文信息展示博文
  • 一个 List 组件,用于接受博文列表展示博文清单

简单的实现一下:

import React from 'react';

export default ({
data
}: {
data: {
title: string;
content: { title: string; content: string }[];
};
}) => {
return (
<div>
<h1>{data.title}</h1>
<div>
{data.content.map(({ title, content }) => (
<article key={title}>
<h2>{title}</h2>
<p>{content}</p>
</article>
))}
</div>
</div>
);
};
import React from 'react';

export default ({ list }: { list: { title: string; key: string }[] }) => {
return (
<div>
<ul>
{list.map(({ title, key }) => (
<li key={title}>
<a href={`/posts/${key}.html`}>{title}</a>
</li>
))}
</ul>
</div>
);
};

构建脚本

然后再来实现下构建脚本,首先构建脚本一样需要支持 JSX 的解析,然后构建脚本读取博文数据文件夹下的所有文件,将其依次解析为静态 HTML,并且再生成一份列表的 HTML

代码也非常简单:

import fs from 'fs';
import { renderToString } from 'react-dom/server';
import React from 'react';
import Post from './ui/Post';
import List from './ui/List'; // get all the files in posts
const posts = fs.readdirSync('posts'); // make sure the dir exists
if (!fs.existsSync('dist')) {
fs.mkdirSync('dist');
}
if (!fs.existsSync('dist/posts')) {
fs.mkdirSync('dist/posts');
} posts.map(post => {
const postInfo = require('./posts/' + post);
const fileName = `dist/posts/${post.replace('.json', '.html')}`;
// make sure file exists
fs.writeFileSync(fileName, `<div id="root">${renderToString(<Post data={postInfo} />)}</div>`);
}); fs.writeFileSync(
'dist/index.html',
`<div id="root">${renderToString(
<List list={posts.map(post => ({ ...require('./posts/' + post), key: post.replace('.json', '') }))} />
)}</div>`
);

首先通过 readdirSync 读取博文信息目录,这里只处理了单层,复杂点的需要递归处理下多层目录结构。然后将每篇博文通过 renderToString 渲染为静态 HTML 并写入文件。最后再输出列表对应的 HTML 文件即可。

通过该脚本可以通过以下的博文:

生成以下 HTML

静态文件服务

HTML 生成后,只需要启动静态文件服务器,将静态 HTML 丢过去即可,可以使用 nginx 或者 serve 包。

更多细节

上面只是一个最简单的例子,要实现一个完整的系统还需要考虑以下几个方面:

  • 样式支持 - 在渲染静态文件时添加上 link 引入即可。
  • hydrate - 本文并没有实现 hydrate,其实实现逻辑差不多,在静态文件渲染时嵌入脚本,然后脚本在客户端进行 hydrate,不过一般的 SSG 场景下动态的内容不多,所以如果没有交互可以忽略。

总结

React 中的 SSG 本质也是通过 renderToString 来实现,但是时机与 SSR 不同,是在构建时进行。

最后

本文的 demo 代码放置在 React SSG Demo 中,可自行取阅。

React SSG - 也写个 Demo 吧的更多相关文章

  1. 手写Spring+demo+思路

    我在学习Spring的时候,感觉Spring是很难的,通过学习后,发现Spring没有那么难,只有你去学习了,你才会发现,你才会进步 1.手写Spring思路: 分为配置.初始化.运行三个阶段如下图 ...

  2. 如何写好demo——学习感悟

    文章标题:教你如何写好Demo应用 如何制作出最有用的demo呢? 简,易 在demo中,我们要专注于单一的主题.我们的教学覆盖了很大的知识范围,因此,化整为零是非常必要的. 例如,我们要说明Andr ...

  3. React深入 - 手写redux api

    简介: 手写实现redux基础api createStore( )和store相关方法 api回顾: createStore(reducer, [preloadedState], enhancer) ...

  4. react深入 - 手写实现react-redux api

    简介:简单实现react-redux基础api react-redux api回顾 <Provider store>把store放在context里,所有子组件可以直接拿到store数据 ...

  5. react动画难写?试试react版transformjs

    简介 transformjs在非react领域用得风生水起,那么react技术栈的同学能用上吗?答案是可以的.junexie童鞋已经造了个react版本. 动画实现方式 传统 web 动画的两种方式: ...

  6. 祭奠一年前写 Direct2D demo

    一年前, 用Direct2D实现了不怎么样的UI库. 用不怎么样的UI库实现了这个Demo. 当时放进某群共享, 以此装逼. 今天无意中翻出来, 解压, 什么都没变, 还是压缩前的模样. 不经意看见被 ...

  7. 用react + redux + router写一个todo

    概述 最近学习redux,打算用redux + router写了一个todo.记录下来,供以后开发时参考,相信对其他人也有用. 注意: 我只实现了Footer组件的router,其它组件的实现方法是类 ...

  8. react比较入门的小demo

    什么是jsx?    JSX是JavaScript  XML 这两个单词的缩写,xml和html非常类似,简单来说可以把它理解成使用各种各样的标签,大家可以自行 百度.所以jsx就是在javascri ...

  9. 在React native 如何写if判断和for循环

    在vue中一般在需要判断时都是通过if语句来实现的,但是在react native中一般则通过三元运算法来实现. 具体代码如下所示. import React from 'react'; import ...

  10. react中怎么写css样式?

    JSX基本语法中关于react如何写css样式主要有三种方法 1.基于class --(className) 基于className ,通过className在style中给该class名的DOM元素 ...

随机推荐

  1. python入门教程之十九cgi编程

    什么是CGI CGI 目前由NCSA维护,NCSA定义CGI如下: CGI(Common Gateway Interface),通用网关接口,它是一段程序,运行在服务器上如:HTTP服务器,提供同客户 ...

  2. 【Note】(坑)一些组合恒等式的实际意义理解(和待填坑的组合数学知识)

    目录 排列组合 恒等式 (1) \(C_n^m=C_n^{n-m}\) (2) \(A_n^m+mA_n^{m-1}=A_{n+1}^m\) (3) \(C_n^{m-1}+C_n^{m}=C_{n+ ...

  3. 【Diary】CSP-S2 2021 游记 & NOIP 备赛发疯日记

    Day 0 两个极端的回跳. .....不行啊. 我快输不起了........... ------------------------------- 早上被生物钟强行唤醒,逼自己懒床到6:40. 弹琴 ...

  4. Python GDAL库在Anaconda环境中的配置

      本文介绍在Anaconda环境下,安装Python中栅格.矢量等地理数据处理库GDAL的方法.   需要注意的是,本文介绍基于conda install命令直接联网安装GDAL库的方法:这一方法有 ...

  5. day120:MoFang:修复宠物喂食饱食度不增加的BUG&修复宠物死亡导致数据错乱的BUG

    目录 BUG1:修复宠物喂食饱食度未增加的BUG BUG2:修复当用户拥有2个宠物时,如果第1个宠物挂了,会出现第二个宠物变成第1个宠物的情况,会导致数据发生混乱出现bug BUG1:修复宠物喂食饱食 ...

  6. 【深度学习】【图像分类网络】(一)残差神经网络ResNet以及组卷积ResNeXt

    ResNet网络 论文:Deep Residual Learning for Image Recognition 网络中的亮点: 1 超深的网络结构(突破了1000层) 上图为简单堆叠卷积层和池化层的 ...

  7. The first week match's conclusion

    自我声讨(不是 这周比赛有难也有易,但是我都是写得很少,摸鱼实在太严重,当然技术不到位也是一个方面,主要还是自己的问题.不再讨论 这周比赛学到.用到的的语法如下 快读 int read() { int ...

  8. Tars-Cpp 协程实现分析

    作者:vivo 互联网服务器团队- Ye Feng 本文介绍了协程的概念,并讨论了 Tars Cpp 协程的实现原理和源码分析. 一.前言 Tars 是 Linux 基金会的开源项目(https:// ...

  9. 如何借助分布式存储 JuiceFS 加速 AI 模型训练

    传统的机器学习模型,数据集比较小,模型的算法也比较简单,使用单机存储,或者本地硬盘就足够了,像 JuiceFS 这样的分布式存储并不是必需品. 随着近几年深度学习的蓬勃发展,越来越多的团队开始遇到了单 ...

  10. VS2022使用ClickOnce发布程序本地安装.net框架

    因为遇到下面的错误,没有在网上搜到详细解决问题的教程,费了一些时间才解决了问题,特此记录一下,也希望能帮助到其他人. 要在"系统必备"对话框中启用"从与我的应用程序相同的 ...