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

Node.js中如何先渲染静态数据,待动态数据就绪后再渲染?

解决思路:分阶段渲染静态与动态内容

你的问题核心是避免页面因慢查询整体阻塞——传统的res.render是服务器端一次性渲染完整页面,必须等所有数据就绪才能返回,所以会导致用户长时间看不到内容。要实现先展示静态内容、再加载动态数据,我们可以把渲染拆成两个独立阶段:


方案1:前端异步加载动态数据(最通用易实现)

这个方案的逻辑非常直观:先让服务器返回包含静态内容的页面框架,等页面在浏览器加载完成后,再通过AJAX请求专门的API获取动态数据,最后把数据渲染到页面预留的占位区域。

步骤1:修改服务器端代码

拆分路由,一个负责返回静态页面,另一个专门提供动态数据的API接口:

// 1. 快速返回包含静态内容的页面
router.get('/', function(req, res){
  // 不需要等待数据库查询,直接渲染静态部分
  res.render('index', {
    'title':'my title',
    'pageHeader': 'my header'
    // 这里不再传递results,留空即可
  });
});

// 2. 新增API接口,专门返回动态数据
router.get('/api/table-data', function(req, res){
  db.connect(function(err, db) {
    if (err) {
      console.log('数据库连接错误');
      return res.status(500).json({error: '加载失败:数据库连接异常'});
    }
    db.query('select * from table', function(err, results){
      if (err) {
        console.log('查询错误');
        return res.status(500).json({error: '加载失败:查询数据异常'});
      }
      // 以JSON格式返回动态数据
      res.json(results);
    });
  });
});

步骤2:修改前端模板(以EJS为例)

在模板里预留动态内容的占位符,同时添加前端JS代码来异步请求并渲染数据:

<!DOCTYPE html>
<html>
<head>
  <title><%= title %></title>
</head>
<body>
  <h1><%= pageHeader %></h1>
  <!-- 动态内容占位区,先显示加载提示 -->
  <div id="dynamic-content">
    <p>数据加载中...</p>
  </div>

  <script>
    // 页面加载完成后触发数据请求
    window.addEventListener('load', function() {
      fetch('/api/table-data')
        .then(response => {
          if (!response.ok) throw new Error('请求失败');
          return response.json();
        })
        .then(results => {
          const contentBox = document.getElementById('dynamic-content');
          // 清空加载提示,开始渲染动态数据
          contentBox.innerHTML = '';
          results.forEach(item => {
            // 根据你的数据结构生成DOM元素,这里示例为简单文本
            const itemElement = document.createElement('div');
            itemElement.textContent = `ID: ${item.id}, 内容: ${item.content}`;
            contentBox.appendChild(itemElement);
          });
        })
        .catch(error => {
          const contentBox = document.getElementById('dynamic-content');
          contentBox.innerHTML = `<p>加载失败:${error.message}</p>`;
        });
    });
  </script>
</body>
</html>

方案2:服务器端流式渲染(进阶场景)

如果你的项目需要纯服务器端渲染动态内容(比如SEO需求),可以用流式输出的方式:让服务器先发送静态部分的HTML,等动态数据就绪后,再发送动态内容的HTML片段补全页面。这种方式需要模板引擎支持流式渲染(比如EJS的renderStream)。

示例代码:

router.get('/', function(req, res){
  // 设置正确的响应头
  res.setHeader('Content-Type', 'text/html');
  
  // 先渲染静态部分并流式发送到客户端
  const stream = res.render('index-stream', {
    'title':'my title',
    'pageHeader': 'my header'
  });
  
  // 静态部分发送完成后,执行数据库查询
  stream.on('finish', function() {
    db.connect(function(err, db) {
      if (err) {
        console.log('数据库连接错误');
        res.write('<div>加载失败:数据库连接异常</div></body></html>');
        return res.end();
      }
      db.query('select * from table', function(err, results){
        if (err) {
          console.log('查询错误');
          res.write('<div>加载失败:查询数据异常</div></body></html>');
          return res.end();
        }
        // 生成动态内容的HTML并发送
        const dynamicHtml = results.map(item => `<div>ID: ${item.id}, 内容: ${item.content}</div>`).join('');
        res.write(`<div id="dynamic-content">${dynamicHtml}</div></body></html>`);
        res.end();
      });
    });
  });
});

对应的index-stream.ejs模板只包含静态部分的前半段:

<!DOCTYPE html>
<html>
<head>
  <title><%= title %></title>
</head>
<body>
  <h1><%= pageHeader %></h1>
  <!-- 这里留空,等待服务器后续发送动态内容 -->

关键说明

  • 方案1是最推荐的实践:用户能快速看到页面框架,体验流畅,而且实现简单,兼容性强,还能复用API接口给其他场景使用。
  • 方案2适合必须服务器端渲染动态内容的场景,但需要处理流式输出的细节(比如确保HTML结构完整),复杂度更高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:30:46