从电商页面秒级加载到表单无刷新提交jQuery AJAX实战教程手把手教你用ajax和get实现前后端数据交互
好,咱们今天聊的话题,可是很多做前端的朋友都绕不开的一道坎。
你想想,以前买过东西没?在淘宝、京东上逛的时候,有没有注意过那种感觉——你点一下”加入购物车”,页面没有跳,也没有转圈,那个商品”嗖”的一下就出现在购物车里了?再比如,你登录的时候,密码输对了,也是瞬间就进去了,连个刷新都没有。
这些东西背后,都有一个共同的名字:AJAX。
今天我就用最接地气的方式,带你把这个东西彻底搞明白。不用害怕,咱们从最基础的讲起,一步步来。
一、什么是AJAX?先搞清楚”异步”这两个字
很多教程一上来就甩定义,什么”异步JavaScript和XML”……说实话,刚学的时候我看了也懵。
咱们换个说法。
想象你在餐厅吃饭。
传统的网页加载方式,就像是你点完菜之后,必须坐在位置上傻等。服务员做好一道菜,端上来,你吃完,再点下一道。每一道菜之间,你都在等,什么也做不了。
而AJAX呢?就像是你点完菜之后,可以在座位上刷刷手机、聊聊天。服务员做好菜了,直接端上来就行,你不用每次都跑回厨房点菜。
“异步”的意思就是:你不用等着,后台可以在你继续做其他事情的时候,悄悄把数据处理好。
那XML又是什么?其实现在已经很少有人用XML了,现在普遍用的是 JSON,一种更轻量的数据格式。所以严格来说,现在的AJAX应该叫”异步JavaScript和JSON”,但名字大家都习惯了,就叫AJAX。
二、jQuery AJAX到底是个啥?
在讲代码之前,先要知道一件事:AJAX不是一门新语言,它就是一种”技术思想”——用JavaScript让网页和服务器偷偷交换数据,然后局部更新页面。
而jQuery呢,是一个JavaScript的库。它把AJAX这摊事儿封装得特别好,让你写起来简单很多。
为什么推荐用jQuery AJAX而不是原生的?
因为原生的XMLHttpRequest写起来真的太麻烦了,各种兼容性问题,代码长得让你怀疑人生。jQuery做了大量的封装工作,你只需要写几行代码就能搞定。
说这么多,咱们直接上手。
三、第一个实战:用GET请求从服务器拉取商品数据
场景设定
假设你正在做一个电商网站,现在需要实现一个功能:用户点击”加载更多商品”,页面自动从服务器获取下一批商品数据,并展示出来,而不需要刷新整个页面。
这就是典型的AJAX GET请求场景。
前端代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>电商商品加载演示</title>
<!-- 引入jQuery -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<style>
.product-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
padding: 20px;
}
.product-item {
border: 1px solid #ddd;
border-radius: 8px;
padding: 15px;
text-align: center;
transition: box-shadow 0.3s;
}
.product-item:hover {
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.product-item img {
width: 100%;
height: 150px;
object-fit: cover;
border-radius: 4px;
}
.product-name {
font-size: 14px;
color: #333;
margin: 10px 0;
}
.product-price {
font-size: 18px;
color: #e4393c;
font-weight: bold;
}
#loading {
text-align: center;
padding: 20px;
color: #999;
}
.load-more-btn {
display: block;
margin: 20px auto;
padding: 12px 40px;
background: #ff6600;
color: white;
border: none;
border-radius: 25px;
font-size: 16px;
cursor: pointer;
}
.load-more-btn:hover {
background: #e55b00;
}
.load-more-btn:disabled {
background: #ccc;
cursor: not-allowed;
}
</style>
</head>
<body>
<h2 style="text-align:center;margin-top:30px;">🛒 限时秒杀商品</h2>
<div class="product-list" id="productList">
<!-- 商品会动态加载到这里 -->
</div>
<div id="loading">点击按钮加载更多商品...</div>
<button class="load-more-btn" id="loadMoreBtn">加载更多商品</button>
<script>
// 当前加载的页码
var currentPage = 1;
var isLoading = false;
// 页面加载完成后,先加载第一页数据
$(document).ready(function() {
loadProducts(1);
});
// 点击"加载更多"按钮
$('#loadMoreBtn').click(function() {
if (isLoading) return; // 防止重复点击
currentPage++;
loadProducts(currentPage);
});
/**
* 核心函数:通过AJAX GET请求从服务器加载商品数据
* @param {number} page - 页码
*/
function loadProducts(page) {
isLoading = true;
$('#loadMoreBtn').prop('disabled', true).text('加载中...');
// 用jQuery的$.ajax方法发起GET请求
$.ajax({
url: '/api/products', // 请求地址
type: 'GET', // 请求方法:GET
data: { page: page }, // 传递给服务器的参数
dataType: 'json', // 期望服务器返回JSON格式
success: function(res) {
// 请求成功后的回调
if (res.code === 200 && res.data.length > 0) {
renderProducts(res.data);
currentPage = page;
// 如果没有更多商品了,隐藏按钮
if (res.data.length < 6) {
$('#loadMoreBtn').hide();
$('#loading').text('已经到底啦~');
}
} else {
$('#loading').text('暂无更多商品');
}
},
error: function(xhr, status, error) {
// 请求失败后的回调
console.error('加载商品失败:', error);
$('#loading').text('加载失败,请刷新重试');
},
complete: function() {
// 请求完成后的回调(无论成功失败都会执行)
isLoading = false;
$('#loadMoreBtn').prop('disabled', false).text('加载更多商品');
}
});
}
/**
* 把商品数据渲染到页面上
* @param {Array} products - 商品数组
*/
function renderProducts(products) {
var $list = $('#productList');
products.forEach(function(product) {
var $item = $(`
<div class="product-item">
<img src="${product.image}" alt="${product.name}">
<div class="product-name">${product.name}</div>
<div class="product-price">¥${product.price}</div>
</div>
`);
$list.append($item);
});
}
</script>
</body>
</html>
后端模拟接口(用Node.js Express)
前端写完了,咱们需要一个后端接口来配合。下面是一个简单的Node.js后端代码,模拟返回商品数据:
const express = require('express');
const app = express();
// 模拟商品数据库
const allProducts = [];
for (let i = 1; i <= 50; i++) {
allProducts.push({
id: i,
name: `超值好物 ${i}`,
price: (Math.random() * 500 + 10).toFixed(2),
image: `https://via.placeholder.com/200x150?text=Product${i}`
});
}
// 模拟分页接口
app.get('/api/products', (req, res) => {
const page = parseInt(req.query.page) || 1;
const pageSize = 6;
const start = (page - 1) * pageSize;
const end = start + pageSize;
const products = allProducts.slice(start, end);
// 返回JSON格式数据
res.json({
code: 200,
message: 'success',
data: products,
total: allProducts.length
});
});
app.listen(3000, () => {
console.log('服务器运行在 http://localhost:3000');
});
运行效果
- 打开前端页面,会自动加载第一页的6个商品
- 点击”加载更多商品”,页面向后默默发送一个GET请求
- 拿到数据后,新的商品卡片出现在页面下方
- 整个过程没有刷新,用户感知不到任何卡顿
四、第二个实战:表单无刷新提交(GET方式)
场景设定
电商网站上有个搜索框,用户输入关键词搜索商品。传统做法是用户输入完点击搜索,整个页面跳转到搜索结果页。
用AJAX的话,用户输入的时候(或者点击搜索按钮),搜索结果就出现在当前页面下方,体验丝滑很多。
前端代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>电商搜索 - AJAX无刷新</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<style>
body { font-family: -apple-system, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
.search-box {
display: flex;
gap: 10px;
margin-bottom: 30px;
}
.search-box input {
flex: 1;
padding: 12px 16px;
border: 2px solid #ddd;
border-radius: 8px;
font-size: 16px;
outline: none;
transition: border-color 0.3s;
}
.search-box input:focus {
border-color: #ff6600;
}
.search-box button {
padding: 12px 24px;
background: #ff6600;
color: white;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
}
.search-box button:hover { background: #e55b00; }
.result-item {
display: flex;
align-items: center;
gap: 15px;
padding: 15px;
border: 1px solid #eee;
border-radius: 8px;
margin-bottom: 10px;
}
.result-item img {
width: 60px;
height: 60px;
object-fit: cover;
border-radius: 4px;
}
.result-info { flex: 1; }
.result-name { font-weight: bold; color: #333; }
.result-price { color: #e4393c; font-size: 16px; margin-top: 4px; }
.loading-text { text-align: center; color: #999; padding: 20px; }
.empty-text { text-align: center; color: #999; padding: 20px; }
</style>
</head>
<body>
<h2>🔍 商品搜索</h2>
<div class="search-box">
<input type="text" id="searchInput" placeholder="输入商品名称关键词...">
<button id="searchBtn">搜索</button>
</div>
<div id="searchResults"></div>
<script>
// 模拟商品数据库
const mockProducts = [
{ id: 1, name: '苹果 iPhone 15 Pro Max', price: '8999', image: 'https://via.placeholder.com/60x60?text=iPhone' },
{ id: 2, name: '华为 Mate 60 Pro', price: '6999', image: 'https://via.placeholder.com/60x60?text=Huawei' },
{ id: 3, name: '小米14 Ultra', price: '5999', image: 'https://via.placeholder.com/60x60?text=Xiaomi' },
{ id: 4, name: 'OPPO Find X7', price: '4999', image: 'https://via.placeholder.com/60x60?text=OPPO' },
{ id: 5, name: 'vivo X100 Pro', price: '4799', image: 'https://via.placeholder.com/60x60?text=vivo' },
{ id: 6, name: ' MacBook Pro 16寸', price: '14999', image: 'https://via.placeholder.com/60x60?text=Mac' },
{ id: 7, name: '联想 ThinkPad X1 Carbon', price: '12999', image: 'https://via.placeholder.com/60x60?text=ThinkPad' },
{ id: 8, name: '戴森 V15 吸尘器', price: '4990', image: 'https://via.placeholder.com/60x60?text=Dyson' },
{ id: 9, name: '索尼 WH-1000XM5 耳机', price: '2499', image: 'https://via.placeholder.com/60x60?text=Sony' },
{ id: 10, name: 'Nintendo Switch OLED', price: '2599', image: 'https://via.placeholder.com/60x60?text=Switch' }
];
/**
* 搜索商品 - 使用AJAX GET请求
* @param {string} keyword - 搜索关键词
*/
function searchProducts(keyword) {
var $results = $('#searchResults');
// 如果没有输入内容,清空结果
if (!keyword.trim()) {
$results.html('<div class="empty-text">请输入搜索关键词</div>');
return;
}
// 显示加载状态
$results.html('<div class="loading-text">正在搜索中...</div>');
// 发起AJAX GET请求
$.ajax({
url: '/api/search',
type: 'GET',
data: { keyword: keyword }, // 参数名要和后端接收的一致
dataType: 'json',
success: function(res) {
if (res.code === 200 && res.data.length > 0) {
// 渲染搜索结果
var html = res.data.map(function(item) {
return `
<div class="result-item">
<img src="${item.image}" alt="${item.name}">
<div class="result-info">
<div class="result-name">${item.name}</div>
<div class="result-price">¥${item.price}</div>
</div>
</div>
`;
}).join('');
$results.html(html);
} else {
$results.html('<div class="empty-text">没有找到相关商品,换个关键词试试?</div>');
}
},
error: function() {
$results.html('<div class="empty-text">搜索失败,请稍后重试</div>');
}
});
}
// 绑定搜索按钮点击事件
$('#searchBtn').click(function() {
var keyword = $('#searchInput').val();
searchProducts(keyword);
});
// 绑定回车键搜索(提升用户体验)
$('#searchInput').keypress(function(e) {
if (e.which === 13) { // 回车键
var keyword = $(this).val();
searchProducts(keyword);
}
});
</script>
</body>
</html>
对应的后端搜索接口
// 搜索接口
app.get('/api/search', (req, res) => {
const keyword = req.query.keyword || '';
// 简单的模糊匹配搜索
const results = mockProducts.filter(item =>
item.name.toLowerCase().includes(keyword.toLowerCase())
);
res.json({
code: 200,
message: 'success',
data: results
});
});
五、深入理解:GET和POST的区别,什么时候用哪个?
很多人(包括我当初)搞不清楚GET和POST的区别,这里用大白话讲清楚:
| 区别 | GET | POST |
|---|---|---|
| 数据位置 | 参数拼在URL后面,如 /api/search?keyword=手机 |
参数放在请求体(body)里,URL看不到 |
| 数据长度 | 有限制,浏览器一般限制2KB~8KB | 基本无限制 |
| 安全性 | 参数暴露在URL里,不安全 | 相对安全(但不是绝对安全) |
| 缓存 | 可以被浏览器缓存 | 不会被缓存 |
| 适用场景 | 查询数据、搜索、获取资源 | 提交数据、修改数据、上传文件 |
记住一个简单的口诀:
只读的、查询的操作用GET;会修改数据的、提交的操作用POST。
比如:
- 搜索商品 → GET ✅
- 加入购物车 → POST ✅
- 获取商品详情 → GET ✅
- 提交订单 → POST ✅
- 登录 → POST ✅(密码不能暴露在URL里)
- 获取订单列表 → GET ✅
六、进阶:更简洁的写法(\(.get和\).post)
jQuery还提供了更简洁的写法,如果你觉得上面的代码有点啰嗦,可以试试这个:
// ===== 简洁的GET写法 =====
$.get('/api/products', { page: 2 }, function(res) {
// 直接在回调里处理数据
console.log(res.data);
});
// 或者用 Promise 写法(更现代)
$.get('/api/products', { page: 2 })
.done(function(res) {
console.log('成功:', res);
})
.fail(function(xhr, status, error) {
console.error('失败:', error);
});
// ===== 简洁的POST写法 =====
$.post('/api/cart', {
productId: 1001,
quantity: 2
}, function(res) {
if (res.code === 200) {
alert('添加成功!');
}
});
不过说实话,日常开发中我还是推荐用完整的 $.ajax() 写法,因为参数更明确,可读性更好,出问题时也更容易排查。
七、实际项目中的常见问题和解决办法
问题1:跨域问题(CORS)
现象:控制台报错 Access-Control-Allow-Origin
原因:前端页面和后端API不在同一个域名/端口下,浏览器安全策略阻止了请求。
解决:
- 后端设置响应头:
res.setHeader('Access-Control-Allow-Origin', '*') - 或者用Nginx反向代理,把前后端都放到同一个域名下
// Node.js后端解决跨域
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
next();
});
问题2:请求频率太高怎么办?
现象:用户疯狂点击搜索按钮,发了几十个请求,服务器快扛不住了。
解决:加一个防抖(debounce),让用户停止输入一段时间后再发请求。
let searchTimer = null;
$('#searchInput').on('input', function() {
var keyword = $(this).val();
// 清除上一次的定时器
clearTimeout(searchTimer);
// 延迟300毫秒后再发请求
searchTimer = setTimeout(function() {
searchProducts(keyword);
}, 300);
});
问题3:请求超时处理
现象:网络慢的时候,用户等半天没反应,也不知道是成功还是失败。
解决:设置 timeout 参数,超时后给用户友好提示。
$.ajax({
url: '/api/products',
type: 'GET',
data: { page: currentPage },
dataType: 'json',
timeout: 5000, // 5秒超时
success: function(res) {
renderProducts(res.data);
},
error: function(xhr, status, error) {
if (status === 'timeout') {
alert('请求超时,请检查网络后重试');
} else {
alert('请求失败,请稍后重试');
}
}
});
八、从头到尾串一遍:一个完整的电商购物车案例
光说不练假把式。咱们来做一个完整的例子:点”加入购物车”按钮,不刷新页面,购物车数字实时更新。
前端代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>电商购物车实战</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, sans-serif; background: #f5f5f5; }
/* 顶部导航栏 */
.navbar {
background: #ff6600;
padding: 15px 30px;
display: flex;
justify-content: space-between;
align-items: center;
color: white;
}
.cart-icon {
position: relative;
cursor: pointer;
font-size: 20px;
}
.cart-badge {
position: absolute;
top: -8px;
right: -12px;
background: #e4393c;
color: white;
border-radius: 50%;
width: 20px;
height: 20px;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
}
/* 商品列表 */
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 20px;
padding: 30px;
max-width: 1200px;
margin: 0 auto;
}
.product-card {
background: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
transition: transform 0.2s, box-shadow 0.2s;
}
.product-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0,0,0,0.12);
}
.product-card img {
width: 100%;
height: 180px;
object-fit: cover;
}
.product-info {
padding: 15px;
}
.product-name {
font-size: 14px;
color: #333;
margin-bottom: 8px;
height: 40px;
overflow: hidden;
}
.product-price {
font-size: 18px;
color: #e4393c;
font-weight: bold;
}
.product-price span { font-size: 12px; }
.add-cart-btn {
width: 100%;
padding: 10px;
background: #ff6600;
color: white;
border: none;
font-size: 14px;
cursor: pointer;
transition: background 0.2s;
}
.add-cart-btn:hover { background: #e55b00; }
.add-cart-btn.added {
background: #52c41a;
}
/* 提示动画 */
@keyframes flyToCart {
0% { opacity: 1; transform: scale(1); }
100% { opacity: 0; transform: scale(0.5) translate(100px, -100px); }
}
.fly-item {
position: fixed;
width: 30px;
height: 30px;
background: #ff6600;
border-radius: 50%;
pointer-events: none;
z-index: 9999;
}
</style>
</head>
<body>
<div class="navbar">
<h2>🛍️ 优品商城</h2>
<div class="cart-icon" id="cartIcon">
🛒 购物车
<span class="cart-badge" id="cartBadge">0</span>
</div>
</div>
<div class="product-grid" id="productGrid">
<!-- 商品由JS动态渲染 -->
</div>
<script>
// 模拟商品数据
const products = [
{ id: 1, name: 'Apple MacBook Pro 14寸 M3芯片', price: 12999, image: 'https://via.placeholder.com/220x180/333/fff?text=MacBook' },
{ id: 2, name: 'Sony WH-1000XM5 降噪耳机', price: 2499, image: 'https://via.placeholder.com/220x180/555/fff?text=Sony' },
{ id: 3, name: '罗技 MX Master 3S 鼠标', price: 699, image: 'https://via.placeholder.com/220x180/777/fff?text=Logitech' },
{ id: 4, name: 'Keychron K8 Pro 机械键盘', price: 599, image: 'https://via.placeholder.com/220x180/999/fff?text=Keyboard' },
{ id: 5, name: '戴森 V15 Detect 吸尘器', price: 4990, image: 'https://via.placeholder.com/220x180/444/fff?text=Dyson' },
{ id: 6, name: '小米空气净化器 4 Pro', price: 1099, image: 'https://via.placeholder.com/220x180/666/fff?text=MiAir' },
{ id: 7, name: '大疆 Mini 3 航拍无人机', price: 3788, image: 'https://via.placeholder.com/220x180/888/fff?text=DJI' },
{ id: 8, name: 'Nintendo Switch OLED 版', price: 2599, image: 'https://via.placeholder.com/220x180/aa0/fff?text=Switch' }
];
let cartCount = 0;
// 渲染商品列表
function renderProducts() {
var html = products.map(function(p) {
return `
<div class="product-card">
<img src="${p.image}" alt="${p.name}">
<div class="product-info">
<div class="product-name">${p.name}</div>
<div class="product-price"><span>¥</span>${p.price}</div>
</div>
<button class="add-cart-btn" data-id="${p.id}">加入购物车</button>
</div>
`;
}).join('');
$('#productGrid').html(html);
}
/**
* 加入购物车 - AJAX POST请求
*/
function addToCart(productId) {
$.ajax({
url: '/api/cart/add',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ productId: productId }),
dataType: 'json',
success: function(res) {
if (res.code === 200) {
// 更新购物车数量
cartCount = res.cartCount;
$('#cartBadge').text(cartCount);
// 按钮反馈
var $btn = $(`.add-cart-btn[data-id="${productId}"]`);
$btn.addClass('added').text('已加入 ✓');
// 0.8秒后恢复按钮
setTimeout(function() {
$btn.removeClass('added').text('加入购物车');
}, 800);
}
},
error: function() {
alert('加入失败,请稍后重试');
}
});
}
// 绑定事件
$(document).ready(function() {
renderProducts();
// 委托事件:动态生成的元素也有效
$(document).on('click', '.add-cart-btn', function() {
var productId = $(this).data('id');
addToCart(productId);
});
});
</script>
</body>
</html>
对应的后端接口
// 模拟购物车数据
let cart = [];
// 添加购物车接口
app.post('/api/cart/add', (req, res) => {
const { productId } = req.body;
// 添加到购物车
cart.push({ productId: productId, addTime: Date.now() });
// 返回更新后的数量
res.json({
code: 200,
message: 'success',
cartCount: cart.length
});
});
九、为什么现在大家都在用Axios而不是jQuery AJAX?
说到这里,可能有些朋友会问:现在都什么年代了,还用jQuery?这不是过时了吗?
确实,现在的趋势是 Axios、fetch API 这些更现代的方案。但是:
- 很多老项目还在用jQuery,你总得能看得懂、维护得了吧?
- jQuery AJAX的原理和Axios完全一样,学会了jQuery AJAX,学Axios就是一天的事
- 面试经常考,面试官问你AJAX,你用jQuery答也没问题
不过我还是建议你学完jQuery AJAX之后,再花半天时间学一下 Axios,因为它更简洁、更强大,支持Promise,是现代前端的标准。
// Axios版添加购物车(对比一下,是不是简洁很多?)
async function addToCartAxios(productId) {
try {
const res = await axios.post('/api/cart/add', { productId });
cartCount = res.data.cartCount;
$('#cartBadge').text(cartCount);
} catch (err) {
alert('加入失败');
}
}
十、总结一下今天学到的东西
今天我们讲了:
- AJAX是什么——异步加载数据,不刷新页面
- GET和POST的区别——查询用GET,提交用POST
- jQuery AJAX的基本写法——
$.ajax()完整写法 - \(.get()和\).post()的简洁写法
- 实际项目中的常见问题——跨域、防抖、超时处理
- 两个完整实战——商品加载 + 表单搜索 + 购物车
最重要的一点:
AJAX不是什么高深莫测的东西,它就是让网页变得”聪明”的技术。传统网页每次操作都要刷新,AJAX让它变成了”应用”而不是”文档”。
你现在去打开淘宝、京东,每次点击、每次搜索,背后都是AJAX在默默工作。理解了原理,再去看那些大厂的代码,你就会发现,其实都是这些基础东西的组合。
有什么不清楚的,随时可以问我。实战出真知,动手敲一遍代码,比看十遍教程都管用!
