上次写了一个 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. cs 保研经验贴 | 综合面试题库

    绝大部分夏令营都需要面试:如果幸运的话,稍微问几个问题就结束了:不幸的话,可能要抓住你做项目.读论文.复现.做 pre 等等. 这篇博客适用于幸运的情况:绝大部分学校的面试题库,都是彼此重合的,并且一 ...

  2. 定时器中断_PWM输出_STM32第三课

    1.TIM2中断,需求:实现LED间隔0.5秒闪烁 1.使用CubeMX设置系统时钟.RCC.LED灯.时钟树等基础操作. 2.配置TIMER2,使能为全局变量,设置优先级.并生成代码. 3.代码编写 ...

  3. 安全测试前置实践1-白盒&黑盒扫描

    作者:京东物流 陈维 一.引言 G.J.Myers在<软件测试的艺术>中提出:从心理学角度来说,测试是一个为了寻找错误而运行程序的过程. 那么安全测试则是一个寻找系统潜在安全问题的过程,通 ...

  4. sql 时间函数

    计算时间间隔 day datediff(大日期, 小日期) SELECT datediff('2009-07-31', '2009-07-30') month, year, second timest ...

  5. Java Stream常见用法汇总,开发效率大幅提升

    本文已经收录到Github仓库,该仓库包含计算机基础.Java基础.多线程.JVM.数据库.Redis.Spring.Mybatis.SpringMVC.SpringBoot.分布式.微服务.设计模式 ...

  6. jenkins 自动化部署 2023

    参考 https://www.jenkins.io/doc/book/installing/docker/ https://blog.csdn.net/zqqiang0307/article/deta ...

  7. islider.js轮播图

    本篇文章地址:https://www.cnblogs.com/Thehorse/p/11601032.html css #iSlider-effect-wrapper { height: 220px; ...

  8. C# implicit隐式转换

    今天看书,上面介绍implicit和explicit相对冷门,用的较少. 这个implicit类型虽然冷门,但真的很有用.我在自己的项目里就用了这个 上Demo, 1 public partial c ...

  9. Python OOP面向对象编程

    OOP 思想: 以模块思想解决工程问题 面向过程 VS 面向对象 由面向过程转向面向对象 例子,我要开一个学校,叫XXX 讲师 学生 班主任 教室 学校 常用名词 OO:面向对象 OOA: 分析 OO ...

  10. vue中让嵌入的iframe完美自适应宽度、高度

    涉及到系统集成的时候,前端我们经常会用到iframe嵌入,但是嵌入的时候经常有不适应的情况,太长或太宽.滚动条... 下面的方法可以做到使嵌入的iframe自适应宽度.高度, 1.嵌入iframe,加 ...