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

如何测量Next.js SSR服务端HTML响应耗时及排查性能瓶颈

Next.js SSR服务端全链路耗时排查方案

一、测量服务端从接收请求到发送HTML的总耗时

方法1:自定义服务器拦截请求

创建自定义服务器文件(如server.js),在请求进入时记录起始时间,响应完成时计算总耗时:

const { createServer } = require('http');
const { parse } = require('url');
const next = require('next');

const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();

app.prepare().then(() => {
  createServer((req, res) => {
    const start = Date.now();
    // 监听响应结束事件
    res.on('finish', () => {
      const totalTime = Date.now() - start;
      console.log(`[${req.method}] ${req.url} - 总耗时: ${totalTime}ms`);
    });
    const parsedUrl = parse(req.url, true);
    handle(req, res, parsedUrl);
  }).listen(3000, (err) => {
    if (err) throw err;
    console.log('> Ready on http://localhost:3000');
  });
});

启动命令改为node server.js(开发环境可加NODE_ENV=development),控制台会输出每个请求的总耗时。

方法2:Pages Router中间件记录

在pages/_middleware.js中注入请求时间追踪:

import { NextResponse } from 'next/server';

export function middleware(req) {
  const start = Date.now();
  // 绑定起始时间到请求上下文,后续环节可复用
  req.startTime = start;
  // 监听响应完成事件打印总耗时
  req.res.on('finish', () => {
    const totalTime = Date.now() - start;
    console.log(`请求 ${req.nextUrl.pathname} 总耗时: ${totalTime}ms`);
  });
  return NextResponse.next();
}

二、拆解SSR各环节的耗时

1. 精准测量getInitialProps执行细节

在页面组件的getInitialProps中拆分各步骤耗时:

Page.getInitialProps = async (ctx) => {
  const propsStart = Date.now();
  
  // 记录API请求耗时
  const apiStart = Date.now();
  const data = await fetch('https://api.example.com/data');
  const apiTime = Date.now() - apiStart;
  
  // 记录业务逻辑耗时
  const bizStart = Date.now();
  // 你的业务处理代码
  const bizTime = Date.now() - bizStart;
  
  const propsTotal = Date.now() - propsStart;
  console.log(`[${ctx.pathname}] getInitialProps总耗时: ${propsTotal}ms | API请求: ${apiTime}ms | 业务逻辑: ${bizTime}ms`);
  
  return { data };
};

2. 覆盖React渲染及Next.js内部流程

通过自定义_document.js扩展Server Timing API,添加渲染阶段的时间标记:

import Document, { Html, Head, Main, NextScript } from 'next/document';

class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const renderStart = Date.now();
    const initialProps = await Document.getInitialProps(ctx);
    const renderTime = Date.now() - renderStart;
    
    // 向响应头添加渲染耗时信息,可在Chrome网络面板查看
    ctx.res.setHeader('Server-Timing', `render;dur=${renderTime}`);
    
    return initialProps;
  }
}

export default MyDocument;

同时在自定义服务器中添加路由匹配环节的耗时记录:

createServer((req, res) => {
  const start = Date.now();
  const routeMatchStart = Date.now();
  const parsedUrl = parse(req.url, true);
  const routeMatchTime = Date.now() - routeMatchStart;
  
  res.on('finish', () => {
    const totalTime = Date.now() - start;
    console.log(`[${req.method}] ${req.url} - 总耗时: ${totalTime}ms | 路由匹配: ${routeMatchTime}ms`);
  });
  
  handle(req, res, parsedUrl);
});

3. 用Node.js性能分析工具定位黑盒环节

手动在关键节点添加性能标记,配合Chrome DevTools分析:

// 自定义服务器请求开始时
performance.mark('server_request_start');

// getInitialProps内部
performance.mark('getInitialProps_start');
// ...你的getInitialProps代码
performance.mark('getInitialProps_end');
performance.measure('getInitialProps总耗时', 'getInitialProps_start', 'getInitialProps_end');

// _document.js渲染阶段
performance.mark('render_start');
const initialProps = await Document.getInitialProps(ctx);
performance.mark('render_end');
performance.measure('React渲染耗时', 'render_start', 'render_end');

// 响应完成时
performance.mark('server_response_end');
performance.measure('服务端总耗时', 'server_request_start', 'server_response_end');

// 打印所有测量结果
const measures = performance.getEntriesByType('measure');
measures.forEach(measure => {
  console.log(`${measure.name}: ${measure.duration.toFixed(2)}ms`);
});

启动时使用NODE_OPTIONS='--inspect' next dev,打开Chrome DevTools的Performance面板录制请求,即可看到自定义标记的耗时区间。

4. 排查全局瓶颈

  • 标记_app.js中getInitialProps的执行时间,检查是否存在全局阻塞代码
  • 验证getInitialProps中是否有重复请求或未缓存的计算逻辑,添加缓存减少重复开销

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 18:05:21