WordPress中jQuery AJAX调用PHP时逐次返回响应实现进度条的方案
实现PHP运行过程中逐次向前端返回响应的方案
核心原理说明
你需要的技术是服务器发送事件(Server-Sent Events,SSE),基于HTTP分块传输编码实现,单请求连接建立后,服务端可以多次向客户端推送数据,无需发起多次AJAX请求,完美适配进度条实时更新的需求。
普通jQuery AJAX的success/done回调仅会在HTTP请求完全结束后触发,本身不支持中途接收阶段性响应,因此无需在原生AJAX能力上做尝试。你之前调用wp_send_json_success()只能拿到一次返回的原因是,该函数底层会调用wp_die()直接终止PHP程序执行,且普通HTTP请求仅允许返回一次完整响应,自然无法获取后续进度。
代码实现
WordPress后端(PHP)修改
需要调整原有AJAX接口的响应头和输出逻辑,关闭输出缓冲,按照SSE协议格式输出数据:
<?php class Test { public function __construct() { add_action('wp_ajax_nopriv_test', [$this, 'ajax_test'], 20); add_action('wp_ajax_test', [$this, 'ajax_test'], 20); } public function ajax_test() { // 关闭压缩和缓冲,避免输出被合并 if (function_exists('apache_setenv')) { apache_setenv('no-gzip', '1'); } @ini_set('zlib.output_compression', 0); @ini_set('implicit_flush', 1); // 清空所有已有的输出缓冲 while (ob_get_level()) { ob_end_clean(); } // 设置SSE协议必填响应头 header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); header('Connection: keep-alive'); $args = [ 'post_type' => 'product', 'post_status' => 'publish', 'posts_per_page' => -1, 'order' => 'ASC', 'orderby' => 'ID', ]; $products = new WP_Query($args); $total_count = $products->found_posts; $current_index = 0; if($products->have_posts()) : while($products->have_posts()): $products->the_post(); global $post; $current_index++; // 按照SSE格式输出数据:每行以data:开头,末尾加两个换行符 echo "data: " . json_encode([ 'progress' => round(($current_index / $total_count) * 100, 2), 'post_id' => $post->ID, 'status' => 'running' ]) . "\n\n"; // 强制冲刷缓冲区,将数据立刻发送到前端 flush(); endwhile; // 推送任务完成标记 echo "data: " . json_encode([ 'status' => 'complete', 'total_processed' => $current_index ]) . "\n\n"; flush(); endif; exit; } } new Test();
前端代码修改
无需使用jQuery AJAX,直接用原生EventSource接口接收SSE推送即可:
// 拼接请求地址,参数规则和原有admin-ajax请求一致 const sseRequestUrl = new URL(admin_ajax_url); sseRequestUrl.searchParams.set('action', 'test'); // 建立SSE长连接 const eventSource = new EventSource(sseRequestUrl); // 监听服务端推送的消息 eventSource.onmessage = function (event) { const res = JSON.parse(event.data); console.log('收到推送数据:', res); // 此处更新进度条UI if (res.progress) { // 示例:更新进度条宽度 // $('.progress-bar').css('width', res.progress + '%'); } // 任务完成后关闭连接 if (res.status === 'complete') { eventSource.close(); console.log('所有任务处理完成'); } }; // 异常处理 eventSource.onerror = function (err) { console.error('连接出错:', err); eventSource.close(); };
注意事项
- 若使用Nginx作为Web服务器,需要关闭对应请求路径的代理缓冲,否则输出会被Nginx缓存后统一返回,无法实时推送,在Nginx配置中添加:
proxy_buffering off; - 不要使用
wp_send_json、wp_send_json_success这类WordPress提供的JSON返回函数,这类函数执行后会主动终止PHP程序,无法继续执行后续任务 - SSE默认仅支持GET请求,若需要传递大量敏感参数,可使用SSE POST polyfill实现POST请求的SSE能力
内容的提问来源于stack exchange,提问作者joshmoto
相关产品推荐
相关产品推荐

