DataTables导出2万+记录报Maximum call stack size exceeded错误
问题根因
抛出Uncaught RangeError: Maximum call stack size exceeded错误的核心原因非常明确:你开启了DataTables的serverSide: true服务端处理模式,默认自带的excelHtml5/csvHtml5导出按钮是纯前端生成文件的逻辑:
- 导出时会尝试拉取符合筛选条件的全量数据到浏览器内存,再通过递归遍历单元格拼接文件结构
- 2000条以内数据时,递归调用深度在JS引擎默认栈阈值范围内,可以正常导出
- 数据量达到2万条级别时,递归深度直接超出JS引擎栈上限,直接抛错。
你之前搜到的所有同场景前端配置修改方案,都是针对客户端全量加载模式的,服务端模式下完全不生效——前端本身就没存储全量数据,硬要在浏览器端拼大体积文件本身就是错误的实现思路,就算临时绕过栈报错,也会因为占满内存导致页面卡顿、崩溃。
落地解决方案
彻底放弃前端生成导出文件的逻辑,改成服务端生成文件+前端触发下载的模式,从根源绕开前端性能瓶颈,不管导出多少条数据都不会触发前端报错。
1. 修改前端DataTables初始化配置
把原有前端导出按钮替换为自定义导出按钮,点击时携带当前筛选参数,直接请求后端导出接口,修改后的完整初始化代码如下:
table = $('#example').DataTable({ dom: 'Bfrtip', buttons: [ 'copyHtml5', // 替换原excelHtml5为自定义Excel导出按钮 { text: '导出Excel', action: function () { // 取当前筛选条件 const reqParams = new URLSearchParams({ FromDate: $('#FromDate').val(), ToDate: $('#ToDate').val(), export_type: 'excel' }); // 用隐藏表单提交,兼容各类浏览器的文件下载 const submitForm = document.createElement('form'); submitForm.method = 'POST'; submitForm.action = 'http://100.250.90.110:8090/srx/voucher/export_credit_vouchers'; submitForm.target = '_blank'; reqParams.forEach((value, key) => { const input = document.createElement('input'); input.name = key; input.value = value; submitForm.appendChild(input); }); document.body.appendChild(submitForm); submitForm.submit(); submitForm.remove(); } }, // 替换原csvHtml5为自定义CSV导出按钮 { text: '导出CSV', action: function () { const reqParams = new URLSearchParams({ FromDate: $('#FromDate').val(), ToDate: $('#ToDate').val(), export_type: 'csv' }); const submitForm = document.createElement('form'); submitForm.method = 'POST'; submitForm.action = 'http://100.250.90.110:8090/srx/voucher/export_credit_vouchers'; submitForm.target = '_blank'; reqParams.forEach((value, key) => { const input = document.createElement('input'); input.name = key; input.value = value; submitForm.appendChild(input); }); document.body.appendChild(submitForm); submitForm.submit(); submitForm.remove(); } }, 'pageLength' ], "pageLength": 25, "processing": true, "serverSide": true, "order": [], "aoColumnDefs": [{ "bSortable": false, "aTargets": [0]}], "bInfo": false, lengthMenu: [ [ 10, 25, 50, -1 ], [ '10 rows', '25 rows', '50 rows', 'Show all' ] ], "ajax": { "url": "http://100.250.90.110:8090/srx/voucher/credit_vouchers_datatable", "type": "POST", "data": function ( data ) { data.FromDate = $('#FromDate').val(); data.ToDate = $('#ToDate').val(); } } });
2. 新增CodeIgniter后端导出接口
在你的Voucher控制器下新增export_credit_vouchers方法,逻辑如下:
- 接收前端传的
FromDate、ToDate、export_type参数 - 复用列表页的查询筛选条件,查询全量符合要求的数据,不要加分页参数
- 导出前临时调整PHP配置,避免大数据量下超内存、超时:
ini_set('memory_limit', '1024M'); set_time_limit(300); - 根据
export_type生成对应格式文件:- 导出CSV:输出带UTF-8 BOM头的CSV内容,设置响应头触发浏览器下载
- 导出Excel:引入PhpSpreadsheet类库生成XLSX格式文件,直接输出到响应流触发下载
注意:查询全量数据时不要用框架的findAll一次性把所有结果加载到内存,可以用chunk分批读取数据写入文件,进一步降低服务端内存占用,支持几十万条以上的超大数据量导出。
方案优势
- 彻底绕开前端JS处理大数据的栈溢出、内存溢出问题,浏览器只负责发起下载请求、接收文件,不会出现卡顿、崩溃问题
- 导出数据量没有前端层面的限制,只要服务端性能足够,导出几十万条都可以正常运行
- 导出逻辑和列表查询复用同一套筛选规则,不会出现导出内容和页面展示不一致的问题
避坑提示
不要尝试以下无效方案:
- 修改DataTables源码调整递归逻辑、强行增大浏览器栈阈值:这类修改只对本地浏览器生效,普通用户访问依然会报错,且大文件生成时会占满用户设备内存
- 配置导出按钮的
serverSide参数让按钮分次拉取数据:拉取完成后依然要在前端拼接生成文件,数据量大了还是会爆栈、卡页
内容的提问来源于stack exchange,提问作者Niranjan Kumar Chowdam
相关产品推荐
相关产品推荐

