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

Node.js EJS模板引擎遍历长数组性能优化求助

解决方案

1. 前端异步加载数据 + 虚拟滚动(推荐)

核心思路:服务端仅渲染空的下拉容器,前端通过API获取完整数据集,再用虚拟滚动技术只渲染可视区域内的选项,避免一次性生成28k+ DOM节点。

  • 服务端改造:

    • 新增API接口返回筛选后的必要字段数据(仅保留下拉所需的value和label,缩减数据体积):
      // Node.js/Express示例
      app.get('/api/options', async (req, res) => {
        try {
          // 只查询下拉需要的字段,转换格式适配前端
          const options = await db.collection('yourCollection')
            .find({}, { _id: 0, value: '$_id', label: '$name' })
            .toArray();
          res.json(options);
        } catch (err) {
          res.status(500).json({ error: err.message });
        }
      });
      
    • EJS模板仅渲染空容器:
      <div class="custom-select" id="largeSelect">
        <input type="text" placeholder="搜索选项" id="selectInput">
        <div class="select-dropdown" id="dropdownContainer"></div>
      </div>
      
  • 前端原生虚拟滚动实现:

    const dropdownContainer = document.getElementById('dropdownContainer');
    const selectInput = document.getElementById('selectInput');
    let allOptions = [];
    const itemHeight = 30; // 自定义每个选项的固定高度
    let visibleCount = 0;
    let startIndex = 0;
    
    // 加载全量数据
    async function loadOptions() {
      const response = await fetch('/api/options');
      allOptions = await response.json();
      calculateVisibleCount();
      renderVisibleItems();
    }
    
    // 计算可视区域能容纳的选项数量
    function calculateVisibleCount() {
      const containerHeight = dropdownContainer.clientHeight;
      visibleCount = Math.ceil(containerHeight / itemHeight) + 1; // 多渲染1个避免滚动时出现空白
    }
    
    // 渲染当前可视范围内的选项
    function renderVisibleItems() {
      const endIndex = Math.min(startIndex + visibleCount, allOptions.length);
      const visibleItems = allOptions.slice(startIndex, endIndex);
      
      dropdownContainer.innerHTML = visibleItems.map(option => 
        `<div class="select-item" style="height: ${itemHeight}px" data-value="${option.value}">${option.label}</div>`
      ).join('');
      
      // 设置容器总高度,让滚动条正常显示
      dropdownContainer.style.height = `${allOptions.length * itemHeight}px`;
    }
    
    // 滚动时更新可视选项
    dropdownContainer.addEventListener('scroll', () => {
      startIndex = Math.floor(dropdownContainer.scrollTop / itemHeight);
      renderVisibleItems();
    });
    
    // 搜索过滤(可选,提升用户体验)
    selectInput.addEventListener('input', (e) => {
      const keyword = e.target.value.toLowerCase();
      allOptions = allOptions.filter(opt => opt.label.toLowerCase().includes(keyword));
      startIndex = 0;
      renderVisibleItems();
    });
    
    // 页面加载完成后初始化
    window.addEventListener('load', loadOptions);
    

2. 优化数据传输体积

无论采用哪种方案,先缩减MongoDB返回的数据量,能大幅降低传输耗时:

  • 查询时仅返回下拉必需的字段,避免传输冗余数据:
    // MongoDB查询示例
    const options = await db.collection('yourCollection')
      .find({}, { _id: 1, name: 1 }) // 只取_id和显示用的name字段
      .toArray();
    
  • 直接将数据转为JSON字符串传给前端,跳过EJS遍历生成大量HTML的步骤:
    <!-- EJS模板中传递数据 -->
    <script>
      const allOptions = <%- JSON.stringify(options) %>;
    </script>
    

3. 服务端分片渲染(兼容无JS场景)

若必须保留服务端渲染,可初始渲染前100个选项,剩余数据在页面加载后通过AJAX补充插入,避免初始HTML体积过大:

  • EJS初始渲染:
    <select id="largeSelect">
      <% options.slice(0, 100).forEach(option => { %>
        <option value="<%= option._id %>"><%= option.name %></option>
      <% }) %>
    </select>
    
  • 前端补充剩余选项:
    window.addEventListener('load', async () => {
      const select = document.getElementById('largeSelect');
      const response = await fetch('/api/options?skip=100');
      const remainingOptions = await response.json();
      
      remainingOptions.forEach(opt => {
        const option = document.createElement('option');
        option.value = opt._id;
        option.textContent = opt.name;
        select.appendChild(option);
      });
    });
    

关键注意事项

  • 禁止服务端一次性渲染28k+ <option>标签,这会导致HTML体积暴涨,浏览器解析和渲染耗时剧增。
  • 虚拟滚动是平衡「加载全量数据」和「性能」的最优解,既保留全量数据的可用性,又不会生成大量DOM节点。
  • 始终优先压缩传输数据,仅传递必要字段,可配合服务端Gzip压缩进一步优化传输速度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 10:55:14