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

36848条MySQL数据PHP+jQuery加载渲染卡顿(耗时2分30秒)优化方案咨询

如何优化3万+数据的Ajax获取与前端渲染性能?

我需要从MySQL数据库中获取36848条数据,之后通过jQuery在网页上格式化并追加渲染。这两个操作总共耗时2分30秒,在此期间页面完全无法进行任何其他操作,根本没法用。我已经尝试过将SELECT查询限制为仅获取所需字段,但处理时间并没有改善,怀疑耗时的环节是jQuery的页面渲染而非SQL查询,请问该怎么优化?

我的PHP函数如下:

<?php 
function get_my_customers() { 
    $req = $bdd->prepare("SELECT * FROM clients ORDER BY raison_sociale"); 
    $req->execute(); 
    $customers = $req->fetchAll(PDO::FETCH_ASSOC); 
    return $customers; 
} 
if(isset($_POST['ajax'])){ 
    $result = get_my_customers(); 
    echo json_encode($result); 
} 
?>

jQuery(Ajax)数据获取与渲染代码:

function get_my_customers(){ 
    var customers; 
    $.ajax({ 
        url: "model/application/*******/customers/get_my_customers.php", 
        async: false, 
        type: "POST", 
        dataType: 'json', 
        data: {ajax: 'true'}, 
        success: function(data) { 
            customers = data; 
        } 
    }); 
    return customers; 
} 
var my_customers = get_my_customers(); 
$('#customers_list').append('<div class="list_card" card="customers_list"></div>'); 
$.each(my_customers, function(key, val){ 
    if(val.enseigne == null){ 
        var enseigne = ''; 
    }else{ 
        var enseigne = ',' + val.enseigne; 
    } 
    $('.list_card[card="customers_list"]').append(''+ 
    '<div class="list_card_element">'+ 
    '<div><b>'+ val.numero_client +'</b></div>'+ 
    '<div>'+ 
    '<b>'+ val.raison_sociale +'</b>' + enseigne + '<br>'+ 
    val.cp_livraison + ', ' + val.adresse_livraison + 
    '</div>'+ 
    '</div>' 
    ); 
}

核心问题分析

首先,你的代码里有两个致命的性能杀手:

  1. 同步Ajax请求(async: false):这会直接阻塞浏览器主线程,导致页面完全无法响应任何操作,直到请求完成——这就是你觉得页面"假死"的直接原因。
  2. 高频DOM操作:循环里每次调用append()都会触发浏览器的重排(reflow)和重绘(repaint),3万多次的DOM操作会把性能拖到谷底,这确实是你耗时的主要原因。

另外,一次性返回3万条数据的JSON,不仅传输时间长,还会占用大量内存,也是性能瓶颈之一。

分步优化方案

1. 立即移除同步Ajax,改用异步模式

把async: false删掉,同步请求在现代浏览器里已经被逐步废弃,而且完全没必要。调整后的Ajax代码可以改成这样:

function get_my_customers(callback){ 
    $.ajax({ 
        url: "model/application/*******/customers/get_my_customers.php", 
        type: "POST", 
        dataType: 'json', 
        data: {ajax: 'true'}, 
        success: function(data) { 
            callback(data); // 用回调处理数据,而不是阻塞等待返回
        } 
    }); 
} 

// 调用方式改成异步回调
get_my_customers(function(my_customers){ 
    // 在这里处理渲染逻辑
    renderCustomers(my_customers);
});

2. 优化前端渲染:减少DOM操作次数

不要每次循环都去操作DOM,而是先把所有HTML内容拼接成一个字符串,或者用DocumentFragment一次性插入DOM,这样只会触发一次重排重绘。

方案一:字符串拼接(简单高效)

function renderCustomers(customers){ 
    var $listCard = $('<div class="list_card" card="customers_list"></div>');
    var html = ''; // 先把所有元素的HTML拼到字符串里
    
    $.each(customers, function(key, val){ 
        var enseigne = val.enseigne ? ',' + val.enseigne : '';
        html += `
            <div class="list_card_element">
                <div><b>${val.numero_client}</b></div>
                <div>
                    <b>${val.raison_sociale}</b>${enseigne}<br>
                    ${val.cp_livraison}, ${val.adresse_livraison}
                </div>
            </div>
        `;
    });
    
    $listCard.html(html); // 一次性把所有内容插入到容器
    $('#customers_list').append($listCard);
}

方案二:使用DocumentFragment(更规范的DOM操作方式)

function renderCustomers(customers){ 
    var $listCard = $('<div class="list_card" card="customers_list"></div>');
    var fragment = document.createDocumentFragment(); // 创建文档碎片,不会触发重排
    
    $.each(customers, function(key, val){ 
        var enseigne = val.enseigne ? ',' + val.enseigne : '';
        var $element = $(`
            <div class="list_card_element">
                <div><b>${val.numero_client}</b></div>
                <div>
                    <b>${val.raison_sociale}</b>${enseigne}<br>
                    ${val.cp_livraison}, ${val.adresse_livraison}
                </div>
            </div>
        `);
        fragment.appendChild($element[0]); // 先把元素加到碎片里
    });
    
    $listCard.append(fragment); // 碎片一次性插入容器
    $('#customers_list').append($listCard);
}

3. 后端优化:分批加载数据(分页/滚动加载)

一次性返回3万条数据太夸张了,用户根本不可能一次性看完所有内容。最好改成分页加载或者滚动加载(无限滚动),每次只返回几十条数据,这样传输和渲染都会快很多。

比如后端改成支持分页:

<?php 
function get_my_customers($page = 1, $limit = 50) { 
    $offset = ($page - 1) * $limit;
    // 只查询需要的字段,你已经试过,但要确保确实只选需要的,不要用SELECT *
    $req = $bdd->prepare("SELECT numero_client, raison_sociale, enseigne, cp_livraison, adresse_livraison FROM clients ORDER BY raison_sociale LIMIT :offset, :limit"); 
    $req->bindParam(':offset', $offset, PDO::PARAM_INT);
    $req->bindParam(':limit', $limit, PDO::PARAM_INT);
    $req->execute(); 
    $customers = $req->fetchAll(PDO::FETCH_ASSOC); 
    return $customers; 
} 
if(isset($_POST['ajax'])){ 
    $page = isset($_POST['page']) ? (int)$_POST['page'] : 1;
    $limit = isset($_POST['limit']) ? (int)$_POST['limit'] : 50;
    $result = get_my_customers($page, $limit); 
    // 可以返回总条数,方便前端判断是否还有更多数据
    echo json_encode([
        'data' => $result,
        'total' => $bdd->query("SELECT COUNT(*) FROM clients")->fetchColumn()
    ]); 
} 
?>

然后前端改成滚动加载,比如监听页面滚动,当滚动到底部时加载下一页:

var currentPage = 1;
var isLoading = false;
var total = 0;

function loadCustomers(page){
    if(isLoading) return;
    isLoading = true;
    
    $.ajax({ 
        url: "model/application/*******/customers/get_my_customers.php", 
        type: "POST", 
        dataType: 'json', 
        data: {ajax: 'true', page: page, limit: 50}, 
        success: function(res) { 
            total = res.total;
            renderCustomers(res.data); // 调用之前的渲染函数,追加内容
            currentPage++;
            isLoading = false;
        } 
    });
}

// 初始化加载第一页
loadCustomers(1);

// 监听滚动事件,加载下一页
$(window).scroll(function(){
    if($(window).scrollTop() + $(window).height() >= $(document).height() - 100){
        if(currentPage * 50 < total){
            loadCustomers(currentPage);
        }
    }
});

// 修改渲染函数,改成追加模式(而不是每次创建新的list_card)
function renderCustomers(customers){ 
    var $listCard = $('.list_card[card="customers_list"]');
    if(!$listCard.length){
        $listCard = $('<div class="list_card" card="customers_list"></div>');
        $('#customers_list').append($listCard);
    }
    
    var html = '';
    $.each(customers, function(key, val){ 
        var enseigne = val.enseigne ? ',' + val.enseigne : '';
        html += `
            <div class="list_card_element">
                <div><b>${val.numero_client}</b></div>
                <div>
                    <b>${val.raison_sociale}</b>${enseigne}<br>
                    ${val.cp_livraison}, ${val.adresse_livraison}
                </div>
            </div>
        `;
    });
    
    $listCard.append(html);
}

4. 其他小优化

  • 缓存DOM选择器:比如把$('.list_card[card="customers_list"]')提前缓存起来,不要每次循环都去查找DOM。
  • 避免在循环里做复杂计算:比如enseigne的判断可以简化成三元表达式,减少代码复杂度。
  • 给数据库表加索引:ORDER BY raison_sociale可以给raison_sociale字段加索引,加快SQL查询速度(虽然你之前说查询不是瓶颈,但加索引没坏处)。

按照这些优化方式,你的页面性能会有质的提升,不仅不会再假死,渲染速度也会快很多。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 12:09:05