经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » React » 查看文章
React ISR 如何实现 - 最后的 Demo
来源:cnblogs  作者:嘿嘿不务正业  时间:2023/6/28 8:50:08  对本文有异议

之前写了两个 demo 讲解了如何实现 SSRSSG,今天再写个 demo 说在 ISR 如何实现。

什么是 ISR

ISRIncremental Static Regeneration 增量静态再生,是指在 SSG 的前提下,可以在收到请求时判定页面是否需要刷新,如果需要则重新构建该页面,这样既拥有了静态页面的优势又可以避免页面长时间未更新导致信息过时。且由于在页面维度验证,所以每次可以只构建特定的页面。

ISR 一般适用于符合 SSG 场景,但是却对页面的时限性有一定要求时。

如何实现

简单的 ISR 实现也很简单,只需要在收到页面请求时按照更新策略判断是否需要需要重新生成页面,如果需要触发页面的构建更新。需要注意一般情况下生成页面不会影响页面的响应,而是后台去做构建。

现在就基于之前写的 SSG demo,做一下改造让其支持 ISR

修改构建脚本

由于 ISR 构建会同时在构建脚本和服务器中触发,所以需要对之前的代码做一些小小的改动。

首先抽离出一个通用的构建函数(由于服务器会使用到尽量避免同步代码):

  1. import fs from 'fs/promises';
  2. import { renderToString } from 'react-dom/server';
  3. import React from 'react';
  4. import Post from './ui/Post';
  5. import List from './ui/List';
  6. async function build(type: 'list'): Promise<void>;
  7. async function build(type: 'post', name: string): Promise<void>;
  8. async function build(type: 'list' | 'post', name?: string) {
  9. if (type === 'list') {
  10. const posts = await fs.readdir('posts');
  11. await fs.writeFile(
  12. 'dist/index.html',
  13. `<div id="root">${renderToString(
  14. <List
  15. list={posts.map(post => {
  16. delete require.cache['posts/' + post];
  17. return { ...require('./posts/' + post), key: post.replace('.json', '') };
  18. })}
  19. />
  20. )}</div>`
  21. );
  22. } else {
  23. delete require.cache['posts/' + name];
  24. const postInfo = require('./posts/' + name);
  25. const fileName = `dist/posts/${name}.html`;
  26. await fs.writeFile(fileName, `<div id="root">${renderToString(<Post data={postInfo} />)}</div>`);
  27. }
  28. }
  29. export default build;

这样就可以通过 build 函数来构建指定的 post 或者 list 页面。

然后再将原先的构建脚本做一下简单的修改:

  1. import fs from 'fs';
  2. import build from './build-util';
  3. // make sure the dir exists
  4. if (!fs.existsSync('dist')) {
  5. fs.mkdirSync('dist');
  6. }
  7. if (!fs.existsSync('dist/posts')) {
  8. fs.mkdirSync('dist/posts');
  9. }
  10. // get all the files in posts
  11. const posts = fs.readdirSync('posts');
  12. (async () => {
  13. for await (const post of posts) {
  14. await build('post', post.replace('.json', ''));
  15. }
  16. await build('list');
  17. })();

服务器

由于 ISR 需要在请求时做是否构建的判定,所以原先的静态服务器方案无法继续使用,我们换成 express 来实现:

  1. import express from 'express';
  2. import path from 'path';
  3. import fs from 'fs';
  4. import build from '../build-util';
  5. const app = express();
  6. const expiresTime = 1000 * 60 * 10;
  7. app.use(function (req, res, next) {
  8. setTimeout(() => {
  9. const filename = req.path.indexOf('.html') >= 0 ? req.path : req.path + 'index.html';
  10. // get the file's create timestamps
  11. fs.stat(path.join('./dist', filename), function (err, stats) {
  12. if (err) {
  13. console.error(err);
  14. return;
  15. }
  16. if (Date.now() - +stats.mtime > expiresTime) {
  17. console.log(filename, 'files expired, rebuilding...');
  18. if (filename === '/index.html') {
  19. build('list');
  20. } else {
  21. build('post', path.basename(filename).replace('.html', ''));
  22. }
  23. }
  24. });
  25. });
  26. next();
  27. });
  28. app.use(express.static('dist'));
  29. app.listen(4000, () => {
  30. console.log('Listening on port 4000');
  31. });

我们增加一个 express 的中间件,让其来判定文件是否过期,这里以十分钟为例,实际场景可按需定义过期判定。这里过期后就会调用 build 文件来重新构建该文件。要注意此处先返回再构建,所以用户不会等待构建,并且此次访问依旧是旧的内容,构建完成后访问的才是新的内容。

picture 1

更多细节

  • 注意给构建任务加锁,避免一个页面过期后多个请求同时触发多个同样的构建任务
  • 给构建任务加队列,避免请求过多时同时出现过多的后台构建任务导致服务器资源问题
  • 可以为每个文件制定特定的过期判定条件,比如 post 源文件的修改时间等等

总结

ISR 对比 SSG 可以有效的控制页面的时效性,但也要付出额外的代价:

  • 需要额外的开发成本
  • 需要额外的服务器资源投入
  • 无法使用一般的静态文件服务器

没有最佳,只有最适合,所以实际场景下还是按需选用。

最后

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

原文链接:https://www.cnblogs.com/zxbing0066/p/17510188.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号