首页 > 其他分享 >React ISR 如何实现 - 最后的 Demo

React ISR 如何实现 - 最后的 Demo

时间:2023-06-27 23:34:22浏览次数:46  
标签:fs ISR posts React 构建 build Demo 页面

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

什么是 ISR

ISRIncremental 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 文件来重新构建该文件。要注意此处先返回再构建,所以用户不会等待构建,并且此次访问依旧是旧的内容,构建完成后访问的才是新的内容。

picture 1

更多细节

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

总结

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

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

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

最后

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

标签:fs,ISR,posts,React,构建,build,Demo,页面
From: https://www.cnblogs.com/zxbing0066/p/17510188.html

相关文章

  • React基础
    一、ReactHook(部分)1.useEffectuseEffect用于处理组件中的effect,通常用于请求数据,事件处理,订阅等相关操作。1.当useEffect没有第二个参数时,useEffect会不停的调用2.当useEffect第二个参数为空数组时,仅在组件挂载和卸载时调用3.当useEffect第二个参数为变量时,例如[co......
  • react的函数式组件中使用ref获取到子组件的方法为undefined
    我暂时遇到了两种情况。第一种情况:useImperativeHandle函数写错useImperativeHandle的第二个参数的返回值是作为ref.current的值,但是我写箭头函数写快了,忘记返回值了。useImperativeHandle(ref,()=>{foo},[foo])//错误,这样没有返回值,所以ref.current为undefineduseImp......
  • HTML Over the wire 框架 Unpoly 和 React 的使用场合比较
    Unpoly是一个轻量级的JavaScript框架,它允许您通过HTMLoverthewire的方式进行Web应用程序开发。HTMLoverthewire是一种网络通信模式,它将由服务器生成的HTML片段作为响应发送给客户端,而不是传统的JSON数据。这样,服务器端可以处理更多逻辑,而客户端可以专注于渲染和......
  • 6月26日java&React学习日记
    今日学习java的异常处理,了解了多try,catch,以及单try多catch(需注意子类在上父类在下)异常捕获的方法。同时也学习了React框架,该框架确实比较好用但上手难度较高,需理解其组件化原理,改框架涉及ES6比较深,需加强js或ts的学习。 ......
  • React实战--利用甘特图和看板,强化Paas平台应用
    概述这是一篇React在kintone上的实战,我们需要利用看板和甘特图来来强化项目管理app。另外这次用到了webpack,想了解基本配置思路的可以看这里项目地址GitHub-kintone-samples/SAMPLE-kintone-ganttchart-kanban-cn:projectmanagerforkintone,usingGanttandKanban效......
  • React实战--利用甘特图和看板,强化Paas平台应用
    ​概述这是一篇React在kintone上的实战,我们需要利用看板和甘特图来来强化项目管理app。另外这次用到了webpack,想了解基本配置思路的可以看这里项目地址GitHub-kintone-samples/SAMPLE-kintone-ganttchart-kanban-cn:projectmanagerforkintone,usingGanttandKan......
  • reactRouter6 整理文档
    1.概述ReactRouter以三个不同的包发布到npm上,它们分别为:2.react-router:路由的核心库,提供了很多的:组件、钩子。3.react-router-dom:包含react-router所有内容,并添加一些专门用于DOM的组件,例如<BrowserRouter>等。4.react-router-native:包括react-router所有......
  • React - 循环滚动
    1.准备工作lettimer=null;//定时器constBoxRef=useRef();//父组件refconstChildRef=useRef();//子组件ref用于包裹数据循环的const[roll,setRoll]=useState(true);//是否滚动//comments是要map的数据2.开始鼠标移入时暂停移出时开始移......
  • React基本引入和JSX语法
    1.1React介绍1.1.1.官网英文官网:https://reactjs.org/中文官网:https://react.docschina.org/1.1.2.介绍描述用于动态构建用户界面的JavaScript库(只关注于视图)由Facebook开源1.1.3.React的特点声明式编码组件化编码ReactNative编写原生应用高效(优秀......
  • 提升项目水平的5个React库
    长话短说在本文中,我们将介绍5个库,它们可以解决React开发中一些最常见的痛点(例如数据获取、样式、可访问性和状态管理),从而对您的React开发体验产生积极影响。(更|多优质内|容:java567点c0m) 介绍掌握React的基础知识很重要。事实上,无需大量额外的库,您就可以取得很大的......