上次写了一个 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. 【踩坑系列】发送微信模板消息返回40165 invalid weapp pagepath

    1. 踩坑经历 最近做了个需求,需要往公司微信公众号推送一个模板消息,并且点击该消息需要跳转到公司小程序的某个页面. 1.1 拿到模板id 既然是发送模板消息,第一步就需要登录微信公众号后台新建模板消 ...

  2. python实现关闭usb功能

    禁用usb和启用usb 一禁用usb自动加载功能 公司内部有时候需要禁用usb接口的文件拷贝,但是打印机,扫描枪等待其他设备的使用,我们应该怎么做呢,很简单,可以通过修改BIOS,注册表和第三方软件实 ...

  3. 四月二十一号Java知识基础

    1.接口本身具有数据成员.抽象方法.默认方法.和静态方法,但它与抽象类不同 1)接口的数据成员都是静态的且必须初始化,即数据成员必须是静态常量 2)接口中除咯声明抽象方法外,还可以定义静态方法 和默认 ...

  4. 在 Rainbond 上使用在线知识库系统zyplayer-doc

    zyplayer-doc 是一款适合企业和个人使用的WIKI知识库管理工具,提供在线化的知识库管理功能,专为私有化部署而设计,最大程度上保证企业或个人的数据安全,可以完全以内网的方式来部署使用它. 当 ...

  5. nginx 极简教程

    什么是 Nginx? Nginx (engine x) 是一款轻量级的 Web 服务器 .反向代理服务器及电子邮件(IMAP/POP3)代理服务器. 什么是反向代理? 反向代理(Reverse Pro ...

  6. C++核心知识回顾(自定义数据类型)

    复习C++ 类 自定义数据类型最灵活的方式就是使用C++的类结构 现在定义一个货币类型Currency: enum signType{PLUS,MINUS}; class Currency { pub ...

  7. python 快速替换csv数据集字符串列表中的表情符号为空,asyncio,re,pandas

    传统的字符串列表替换字符串使用遍历非常慢 比如下面这段代码,如果处理几十万或上百万的数据集时,会非常的慢,几小时几天都可能 import re p = re.compile(u'['u'\U0001F ...

  8. 一文搞懂 x64 IA-64 AMD64 Inte64 IA-32e 架构之间的关系

    想要搞清楚 x64.IA64.AMD64 指令集之间的关系,就要先了解 Intel 和 AMD 这两家公司在生产处理器上的发展历史. x86 处理器 1978年 Intel 生产了它的第一款 16bi ...

  9. 初等数论——素数,逆元,EXGCD有关

    初等数论 素数定义 设整数 \(p\ne 0,\pm 1\) .如果 \(p\) 除了平凡约数以外没有其他约数,那么称 \(p\) 为素数(不可约数). 若整数 \(a\ne 0,\pm 1\) 且 ...

  10. 【CF】掉分总结

    比赛总结 前情提要 自从前段时间连续掉分,就心态崩了,还是自己太菜,一直想写个总结,看看这几场比赛都干了啥,以后准备怎么办.鸽了这么久的总结,是该写写了. 这是正文 首先大致提一下情感曲线(菜的真实) ...