React SSG - 也写个 Demo 吧
上次写了一个 SSR 的 DEMO,今天写个小 Demo 来从头实现一下 react 的 SSG,来理解下 SSG 是如何实现的。
什么是 SSG
SSG 即 Static Site Generation 静态站点生成,是指将在构建时就提前生成静态 HTML 页面,速度很快,一般用于以下场景:
SEO(搜索引擎优化):由于部分搜索引擎对CSR内容支持不佳,所以SSG可以提升网站在搜索引擎结果中的排名。- 静态站点:比如博客、
CMS系统输出站点等,由于内容以静态内容居多,都可以使用SSG。
如何实现
简单的 SSG 和 SSR 实现原理差不多,只是时机不同: 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 吧的更多相关文章
- 手写Spring+demo+思路
我在学习Spring的时候,感觉Spring是很难的,通过学习后,发现Spring没有那么难,只有你去学习了,你才会发现,你才会进步 1.手写Spring思路: 分为配置.初始化.运行三个阶段如下图 ...
- 如何写好demo——学习感悟
文章标题:教你如何写好Demo应用 如何制作出最有用的demo呢? 简,易 在demo中,我们要专注于单一的主题.我们的教学覆盖了很大的知识范围,因此,化整为零是非常必要的. 例如,我们要说明Andr ...
- React深入 - 手写redux api
简介: 手写实现redux基础api createStore( )和store相关方法 api回顾: createStore(reducer, [preloadedState], enhancer) ...
- react深入 - 手写实现react-redux api
简介:简单实现react-redux基础api react-redux api回顾 <Provider store>把store放在context里,所有子组件可以直接拿到store数据 ...
- react动画难写?试试react版transformjs
简介 transformjs在非react领域用得风生水起,那么react技术栈的同学能用上吗?答案是可以的.junexie童鞋已经造了个react版本. 动画实现方式 传统 web 动画的两种方式: ...
- 祭奠一年前写 Direct2D demo
一年前, 用Direct2D实现了不怎么样的UI库. 用不怎么样的UI库实现了这个Demo. 当时放进某群共享, 以此装逼. 今天无意中翻出来, 解压, 什么都没变, 还是压缩前的模样. 不经意看见被 ...
- 用react + redux + router写一个todo
概述 最近学习redux,打算用redux + router写了一个todo.记录下来,供以后开发时参考,相信对其他人也有用. 注意: 我只实现了Footer组件的router,其它组件的实现方法是类 ...
- react比较入门的小demo
什么是jsx? JSX是JavaScript XML 这两个单词的缩写,xml和html非常类似,简单来说可以把它理解成使用各种各样的标签,大家可以自行 百度.所以jsx就是在javascri ...
- 在React native 如何写if判断和for循环
在vue中一般在需要判断时都是通过if语句来实现的,但是在react native中一般则通过三元运算法来实现. 具体代码如下所示. import React from 'react'; import ...
- react中怎么写css样式?
JSX基本语法中关于react如何写css样式主要有三种方法 1.基于class --(className) 基于className ,通过className在style中给该class名的DOM元素 ...
随机推荐
- 开发轻量级REST API样板 基于Node.js、MongoDB 通过Mongoose驱动
ZY.Node.Mongodb https://gitee.com/Z568_568/node.mongodb.git https://github.com/ZHYI-source/ZY.Node.M ...
- python入门教程之十五获取对象属性的几种方法
当我们拿到一个对象的引用时,如何知道这个对象是什么类型.有哪些方法呢? 使用type() 首先,我们来判断对象类型,使用type()函数: 基本类型都可以用type()判断: >>> ...
- 通过 iframe 调用 天气预报&jsonp
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- Go语言实现基于HTTP的内存缓存服务
所有的缓存数据都存储在服务器的内存中,因此重启服务器会导致数据丢失,基于HTTP通信会将使开发变得简单,但性能不会太好 缓存服务接口 本程序采用REST接口,支持设置(Set).获取(Get)和删除( ...
- SRE中的SLA/SLO/SLI
SLA通俗理解 SLA 表征服务方与客户间的服务等级协议,定义服务方需保证的服务质量以及不达标情况下的服务补偿,在SRE领域,SLA 细分为 SLI.SLO 与 SLA: SLI,服务质量指标,服务的 ...
- 【总结】从++i思考计算机原子性和线程安全
在C++中,++i被认为是一种原子性操作,即不可分割的.不可中断的整体.它能够确保对变量的修改完整且正确,从而避免了数据竞争等问题,提高了程序的并发性和可靠性.然而,有些人可能会将原子性和线程安全混淆 ...
- live555中ts流详细解析
live555中ts流详细解析 该文档主要是对live555源码下testProgs中testMPEG2TransportStreamer服务器端的详细分析.主要分析ts流实现的总体调用流程.(重新整 ...
- I-o-C 一篇概览
一.ioC 容器和 Bean介绍 IoC(Inversion of Control )也被称之为 DI(dependency injection),名称侧重点略有不同. 所谓控制翻转即对象通过构造函数 ...
- 关于 Bash 脚本中 Shebang 的趣事
哈喽大家好,我是咸鱼 不知道小伙伴们在写 Bash 脚本或者说看别人的 Bash 脚本的时候有没有注意过脚本的第一行 #!/bin/bash Bash 脚本的第一行往往以 #! 开头,这一行称作 sh ...
- 2022-08-21:以下go语言代码输出什么?A:0;B:panic;C:不知道。 package main var n = -99 func main() { m := make(map[
2022-08-21:以下go语言代码输出什么?A:0:B:panic:C:不知道. package main var n = -99 func main() { m := make(map[stri ...