优化原生JS构建HTML表格的加载速度及分页实现咨询
优化表格渲染速度 + 实现分页方案
一、先解决渲染慢的核心问题
原代码循环中反复修改tbody.innerHTML会触发浏览器频繁重排重绘,这是性能瓶颈。改成先拼接所有HTML字符串,一次性插入DOM,能大幅提升渲染效率:
const getdata = async () => { const endpoint = "https://my.api.here/beta?nurse=", response = await fetch(endpoint + x), data = await response.json(), Items = data.Items; tbody.innerHTML = ""; // 用数组拼接HTML,避免多次字符串拼接的性能损耗 let rowsHtml = []; Items.forEach(itemObj => { let { bed, patient_name, status, assigned_nurse_id, need_urgency, need, assigned_nurse, ID, timestamp_created_epoch, patient_id, timestamp_created, room } = itemObj; rowsHtml.push(`<tr> <td>${bed}</td> <td>${patient_name}</td> <td>${status}</td> <td>${assigned_nurse_id}</td> <td>${need_urgency}</td> <td>${need}</td> <td>${assigned_nurse}</td> <td>${ID}</td> <td>${timestamp_created_epoch}</td> <td>${patient_id}</td> <td>${timestamp_created}</td> <td>${room}</td> </tr>`); }); // 一次性插入所有行 tbody.innerHTML = rowsHtml.join(''); // 排序逻辑后续适配分页改造 }
二、实现前端分页功能
1. 添加分页控件HTML
在表格下方插入分页操作区域:
<div class="pagination-controls mt-3"> <button id="prev-btn" class="btn btn-sm btn-secondary" disabled>上一页</button> <span id="page-info" class="mx-2">第 1 页 / 共 0 页</span> <button id="next-btn" class="btn btn-sm btn-secondary" disabled>下一页</button> <select id="page-size" class="form-select form-select-sm ms-2"> <option value="20">20条/页</option> <option value="50">50条/页</option> <option value="100">100条/页</option> </select> </div>
2. 改造JS逻辑,加入分页状态管理
全局保存数据和分页状态,实现分页切换、排序适配:
const selectElement = document.querySelector('.nurses'); const tbody = document.querySelector('#tbody'); const prevBtn = document.querySelector('#prev-btn'); const nextBtn = document.querySelector('#next-btn'); const pageInfo = document.querySelector('#page-info'); const pageSizeSelect = document.querySelector('#page-size'); var x = ''; // 分页状态变量 let allData = []; // 保存所有原始数据 let currentPage = 1; let pageSize = 20; selectElement.addEventListener('change', (event) => { const result = document.querySelector('.result'); x = event.target.value; result.textContent = `You Chose ${event.target.value}`; // 切换护士时重置分页状态 currentPage = 1; getdata(x); }); // 每页条数切换事件 pageSizeSelect.addEventListener('change', () => { pageSize = parseInt(pageSizeSelect.value); currentPage = 1; renderPage(); }); // 上一页按钮事件 prevBtn.addEventListener('click', () => { if (currentPage > 1) { currentPage--; renderPage(); } }); // 下一页按钮事件 nextBtn.addEventListener('click', () => { const totalPages = Math.ceil(allData.length / pageSize); if (currentPage < totalPages) { currentPage++; renderPage(); } }); const getdata = async () => { const endpoint = "https://my.api.here/beta?nurse=", response = await fetch(endpoint + x), data = await response.json(); allData = data.Items; // 缓存所有数据 renderPage(); // 渲染第一页 bindSortEvents(); // 绑定排序事件 }; // 渲染当前页数据 const renderPage = () => { tbody.innerHTML = ""; const startIndex = (currentPage - 1) * pageSize; const endIndex = startIndex + pageSize; const currentPageData = allData.slice(startIndex, endIndex); let rowsHtml = []; currentPageData.forEach(itemObj => { let { bed, patient_name, status, assigned_nurse_id, need_urgency, need, assigned_nurse, ID, timestamp_created_epoch, patient_id, timestamp_created, room } = itemObj; rowsHtml.push(`<tr> <td>${bed}</td> <td>${patient_name}</td> <td>${status}</td> <td>${assigned_nurse_id}</td> <td>${need_urgency}</td> <td>${need}</td> <td>${assigned_nurse}</td> <td>${ID}</td> <td>${timestamp_created_epoch}</td> <td>${patient_id}</td> <td>${timestamp_created}</td> <td>${room}</td> </tr>`); }); tbody.innerHTML = rowsHtml.join(''); // 更新分页信息和按钮状态 const totalPages = Math.ceil(allData.length / pageSize); pageInfo.textContent = `第 ${currentPage} 页 / 共 ${totalPages} 页`; prevBtn.disabled = currentPage === 1; nextBtn.disabled = currentPage === totalPages || allData.length === 0; }; // 绑定表头排序事件(适配分页) const bindSortEvents = () => { // 先移除旧监听,避免重复绑定 document.querySelectorAll('th').forEach(th => th.removeEventListener('click', sortHandler)); document.querySelectorAll('th').forEach(th => th.addEventListener('click', sortHandler)); }; const sortHandler = function() { const idx = Array.from(this.parentNode.children).indexOf(this); this.asc = !this.asc; // 切换升降序 // 对全部数据排序,而非当前页 allData.sort((a, b) => { const keys = ['bed', 'patient_name', 'status', 'assigned_nurse_id', 'need_urgency', 'need', 'assigned_nurse', 'ID', 'timestamp_created_epoch', 'patient_id', 'timestamp_created', 'room']; const getValue = (obj) => { const value = obj[keys[idx]]; return !isNaN(value) ? Number(value) : value.toString(); }; const v1 = getValue(this.asc ? a : b); const v2 = getValue(this.asc ? b : a); return typeof v1 === 'number' ? v1 - v2 : v1.localeCompare(v2); }); currentPage = 1; // 排序后回到第一页 renderPage(); };
3. 方案说明
- 缓存全部数据到
allData,分页时仅渲染当前页数据,减少DOM元素数量,提升页面流畅度 - 排序操作针对全部数据进行,排序后自动回到第一页
- 分页控件状态随当前页自动更新,禁用无效操作按钮
- 支持自定义每页显示条数
三、额外优化建议
- 若数据量持续增长,前端分页会有内存压力,可改为后端分页:请求API时携带
page和pageSize参数,由后端返回对应页数据 - 添加加载状态提示,在请求数据时显示“加载中”,提升用户体验
内容的提问来源于stack exchange,提问作者myt
相关产品推荐
相关产品推荐

