You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node+Express全页缓存打孔:如何将另一路由渲染结果注入当前响应

实现方案

优先选择:异步流程改造(完全可行,无性能问题)

Express原生支持异步处理逻辑,不需要强行将异步请求转同步,你原有的代码仅需补充async/await语法适配即可正常运行,不需要调整整体架构。

你原有代码的核心问题是调用getRemoteInclude时没有等待异步请求执行完成,直接把Promise对象当成了字符串使用,修改后的可运行代码如下:

async function getRemoteInclude(req, content) {
    // remote include format: {{ includeRemote('path') }}
    const handlebarRegex = '(?<={{)(.*?)(?=}})';
    const includeRegex = '(?<=includeRemote[(])(.*?)(?=[)])';
    const replaceHandlebarRegex = /\{\{[^\}]+\}\}/g;
    let parsedContent = content;

    let foundHandlebars = content.match(handlebarRegex);

    if (foundHandlebars) {
        let foundInclude = foundHandlebars[0].match(includeRegex);
        if (foundInclude) {
            const axios = require('axios');
            let path = 'http://expresscache.com:3000/test';
            let response = await axios.get(path);
            // 修复axios返回值字段错误:响应内容存放在data字段而非body
            return parsedContent.replace(replaceHandlebarRegex, response.data);
        }
    }
    return content;
}

function interceptRender() {
    return function (req, res, next) {
        // 给重写的render方法加async标识支持异步逻辑
        res.render = async function (view, options, callback) {
            var self = this;
            options = options || res.locals;

            // 将回调形式的app.render包装为Promise调用
            const renderedContent = await new Promise((resolve, reject) => {
                self.app.render(view, options, (err, content) => {
                    if (err) return reject(err);
                    resolve(content);
                })
            });

            if (!callback) {
                // 等待远程内容注入完成再返回响应
                const finalContent = await getRemoteInclude(req, renderedContent);
                self.locals.body = finalContent;
                self.send(finalContent);
            } else {
                callback(null, renderedContent);
            }
        }
        next();
    }
}

module.exports = {
    interceptRender: interceptRender
};

逻辑优化建议

同一服务内部不需要通过HTTP请求调用路由,可以把对应路由的渲染逻辑抽成公共函数直接调用,避免不必要的网络开销,性能比发HTTP请求高10倍以上。

备选方案:同步HTTP请求(不推荐,仅作调试使用)

如果强行需要同步执行请求,可以使用sync-request库,但该方案会阻塞Node事件循环,高并发场景下会导致所有请求卡顿,完全不适合生产环境使用。

其他替代实现思路

  • 缓存分块:将页面拆分为独立缓存块,每个块单独配置缓存TTL,渲染时直接拼装各块内容,比正则匹配替换的方案更易维护
  • 服务端包含(SSI):如果用Nginx做反向代理,可以直接使用SSI指令实现动态内容注入,不需要在Node层处理,性能更高
  • 边缘端包含(ESI):如果有CDN层,支持ESI的CDN可以直接识别ESI标签自动拼装内容,不需要修改服务端逻辑

内容的提问来源于stack exchange,提问作者poor_documentation

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 12:15:02