详解React ISR如何实现Demo
什么是 ISR
之前写了两个 demo
讲解了如何实现 SSR
和 SSG
,今天再写个 demo
说在 ISR
如何实现。
ISR
即 Incremental Static Regeneration
增量静态再生,是指在 SSG
的前提下,可以在收到请求时判定页面是否需要刷新,如果需要则重新构建该页面,这样既拥有了静态页面的优势又可以避免页面长时间未更新导致信息过时。且由于在页面维度验证,所以每次可以只构建特定的页面。
ISR
一般适用于符合 SSG
场景,但是却对页面的时限性有一定要求时。
如何实现
简单的 ISR
实现也很简单,只需要在收到页面请求时按照更新策略判断是否需要需要重新生成页面,如果需要触发页面的构建更新。需要注意一般情况下生成页面不会影响页面的响应,而是后台去做构建。
现在就基于之前写的 SSG demo
,做一下改造让其支持 ISR
。
修改构建脚本
由于 ISR
构建会同时在构建脚本和服务器中触发,所以需要对之前的代码做一些小小的改动。
首先抽离出一个通用的构建函数(由于服务器会使用到尽量避免同步代码):
import fs from 'fs/promises'; import { renderToString } from 'react-dom/server'; import React from 'react'; import Post from './ui/Post'; import List from './ui/List'; async function build(type: 'list'): Promise<void>; async function build(type: 'post', name: string): Promise<void>; async function build(type: 'list' | 'post', name?: string) { if (type === 'list') { const posts = await fs.readdir('posts'); await fs.writeFile( 'dist/index.html', `<div id="root">${renderToString( <List list={posts.map(post => { delete require.cache['posts/' + post]; return { ...require('./posts/' + post), key: post.replace('.json', '') }; })} /> )}</div>` ); } else { delete require.cache['posts/' + name]; const postInfo = require('./posts/' + name); const fileName = `dist/posts/${name}.html`; await fs.writeFile(fileName, `<div id="root">${renderToString(<Post data={postInfo} />)}</div>`); } } export default build;
这样就可以通过 build
函数来构建指定的 post
或者 list
页面。
然后再将原先的构建脚本做一下简单的修改:
import fs from 'fs'; import build from './build-util'; // make sure the dir exists if (!fs.existsSync('dist')) { fs.mkdirSync('dist'); } if (!fs.existsSync('dist/posts')) { fs.mkdirSync('dist/posts'); } // get all the files in posts const posts = fs.readdirSync('posts'); (async () => { for await (const post of posts) { await build('post', post.replace('.json', '')); } await build('list'); })();
服务器
由于 ISR
需要在请求时做是否构建的判定,所以原先的静态服务器方案无法继续使用,我们换成 express
来实现:
import express from 'express'; import path from 'path'; import fs from 'fs'; import build from '../build-util'; const app = express(); const expiresTime = 1000 * 60 * 10; app.use(function (req, res, next) { setTimeout(() => { const filename = req.path.indexOf('.html') >= 0 ? req.path : req.path + 'index.html'; // get the file's create timestamps fs.stat(path.join('./dist', filename), function (err, stats) { if (err) { console.error(err); return; } if (Date.now() - +stats.mtime > expiresTime) { console.log(filename, 'files expired, rebuilding...'); if (filename === '/index.html') { build('list'); } else { build('post', path.basename(filename).replace('.html', '')); } } }); }); next(); }); app.use(express.static('dist')); app.listen(4000, () => { console.log('Listening on port 4000'); });
我们增加一个 express
的中间件,让其来判定文件是否过期,这里以十分钟为例,实际场景可按需定义过期判定。这里过期后就会调用 build
文件来重新构建该文件。要注意此处先返回再构建,所以用户不会等待构建,并且此次访问依旧是旧的内容,构建完成后访问的才是新的内容。
更多细节
- 注意给构建任务加锁,避免一个页面过期后多个请求同时触发多个同样的构建任务
- 给构建任务加队列,避免请求过多时同时出现过多的后台构建任务导致服务器资源问题
- 可以为每个文件制定特定的过期判定条件,比如
post
源文件的修改时间等等
总结
ISR
对比 SSG
可以有效的控制页面的时效性,但也要付出额外的代价:
- 需要额外的开发成本
- 需要额外的服务器资源投入
- 无法使用一般的静态文件服务器
没有最佳,只有最适合,所以实际场景下还是按需选用。
本文的 demo
代码放置在 React ISR Demo 中,可自行取阅。
以上就是详解React ISR如何实现 Demo的详细内容,更多关于React ISR实现的资料请关注脚本之家其它相关文章!
相关文章
在react-router4中进行代码拆分的方法(基于webpack)
这篇文章主要介绍了在react-router4中进行代码拆分的方法(基于webpack),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧2018-03-03ES6 class类链式继承,实例化及react super(props)原理详解
这篇文章主要介绍了ES6 class类链式继承,实例化及react super(props)原理,结合实例形式详细分析了ES6 中class类链式继承,实例化及react super(props)原理相关概念、原理、定义与使用技巧,需要的朋友可以参考下2020-02-02React onBlur回调中使用document.activeElement返回body完美解决方案
这篇文章主要介绍了React onBlur回调中使用document.activeElement返回body完美解决方案,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2023-04-04react16.8.0以上MobX在hook中的使用方法详解
这篇文章主要为大家介绍了react16.8.0以上MobX在hook中的使用方法详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪2023-07-07从零搭建Webpack5-react脚手架的实现步骤(附源码)
本文主要介绍了从零搭建Webpack5-react脚手架的实现步骤,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下2021-08-08react函数组件useState异步,数据不能及时获取到的问题
这篇文章主要介绍了react函数组件useState异步,数据不能及时获取到的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2022-08-08
最新评论