基于NodeJS、Express、EJS,如何在内容渲染时显示加载页面?
解决EJS渲染期间无法显示加载页面的方案
当前流程是服务器完成所有数据查询、EJS渲染后才返回完整HTML,导致浏览器一直处于等待状态,无法展示加载反馈。可以通过拆分请求流程来解决:
1. 修改页面路由,先返回带加载动画的基础页面
把原/page路由改成先返回仅包含加载UI和前端请求逻辑的EJS模板,让浏览器立刻收到响应并显示加载状态:
app.get("/page", (req, res) => { // 直接返回加载页面,不执行耗时查询 res.render("loading-page"); });
loading-page.ejs示例:
<!DOCTYPE html> <html> <head> <title>加载中...</title> <style> .loading-spinner { border: 4px solid #f3f3f3; border-top: 4px solid #3498db; border-radius: 50%; width: 40px; height: 40px; animation: spin 1s linear infinite; margin: 50px auto; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } #content-area { display: none; max-width: 800px; margin: 0 auto; padding: 20px; } .error-message { color: #dc3545; text-align: center; display: none; margin-top: 20px; } </style> </head> <body> <div class="loading-spinner"></div> <p style="text-align:center;">正在加载数据,请稍候...</p> <div id="content-area"> <!-- 后续通过JS渲染内容 --> </div> <div class="error-message" id="error">加载失败,请刷新重试</div> <script> window.onload = async () => { const spinner = document.querySelector('.loading-spinner'); const contentArea = document.getElementById('content-area'); const errorDiv = document.getElementById('error'); try { // 发起数据请求 const response = await fetch('/api/page-data'); if (!response.ok) throw new Error('请求异常'); const data = await response.json(); // 隐藏加载动画,渲染内容 spinner.style.display = 'none'; contentArea.style.display = 'block'; // 用JS生成页面内容(可根据实际数据结构调整) contentArea.innerHTML = ` <h1>${data.name}</h1> <p>ID:${data.id}</p> <p>描述:${data.description}</p> `; } catch (err) { // 显示错误提示 spinner.style.display = 'none'; errorDiv.style.display = 'block'; console.error(err); } }; </script> </body> </html>
2. 新增API路由处理耗时的数据查询
专门写一个API接口执行原有的数据库查询和数据转换逻辑,返回JSON格式的数据:
app.get("/api/page-data", async (req, res) => { try { // 执行耗时的数据库查询、数据转换操作 const data = await fetchDataFromDatabase(); res.json(data); } catch (err) { // 捕获错误,返回错误状态码和信息 res.status(500).json({ error: '数据加载失败' }); } }); // 模拟耗时查询函数,替换为你的实际逻辑 async function fetchDataFromDatabase() { // 比如用await执行SQL查询、数据处理 await new Promise(resolve => setTimeout(resolve, 3000)); return { id: 1, name: "示例数据", description: "这是通过API返回的内容" }; }
3. 额外优化点
- 添加请求超时处理:避免用户无限等待,超过时间后提示超时
// 修改前端fetch逻辑,加入超时控制 window.onload = async () => { const spinner = document.querySelector('.loading-spinner'); const contentArea = document.getElementById('content-area'); const errorDiv = document.getElementById('error'); const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 30000); // 30秒超时 try { const response = await fetch('/api/page-data', { signal: controller.signal }); clearTimeout(timeoutId); if (!response.ok) throw new Error('请求异常'); const data = await response.json(); // 渲染逻辑... } catch (err) { clearTimeout(timeoutId); spinner.style.display = 'none'; if (err.name === 'AbortError') { errorDiv.textContent = '加载超时,请刷新重试'; } else { errorDiv.textContent = '加载失败,请刷新重试'; } console.error(err); } }; - 保留EJS模板能力:如果习惯用EJS语法渲染,可在前端引入EJS库,用前端模板渲染内容
<!-- 引入前端EJS库 --> <script src="https://cdn.jsdelivr.net/npm/ejs@3.1.9/ejs.min.js"></script> <!-- 定义EJS模板 --> <script type="text/ejs" id="content-template"> <h1><%= name %></h1> <p>ID:<%= id %></p> <p>描述:<%= description %></p> </script> <!-- 修改JS渲染部分 --> const template = document.getElementById('content-template').innerHTML; const renderedHtml = ejs.render(template, data); contentArea.innerHTML = renderedHtml;
内容的提问来源于stack exchange,提问作者KaizOffical
相关产品推荐
相关产品推荐

