欢迎光临
我们一直在努力

前端必会实战|手把手带你写一个购物车(添加/删除/计算总价),附完整代码!

前言

每次逛淘宝、京东,把商品加入购物车,总价自动更新;点击删除,商品消失,金额减少——这些看似简单的交互,背后其实包含了前端最核心的三大技能:数据管理、DOM操作、事件处理。

今天我们就用纯原生 JavaScript,从零开始实现一个极简但完整的购物车。你不仅能学会如何操作数组、渲染页面,还能掌握真实项目中常用的编程思想。

最终效果(建议用浏览器打开感受一下):

  • ✅ 输入商品名称和价格,点击“添加”即可加入购物车。

  • ✅ 每个商品右侧有“删除”按钮,点击立刻移除。

  • ✅ 购物车总价实时更新,删除后自动重新计算。


一、完整代码(复制即用)

将以下代码保存为一个.html文件,用浏览器打开即可体验。

html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>极简购物车 · 实战案例</title>
<style>
/* 简洁样式,重点看JS逻辑 */
.cart-container {
width: 500px;
margin: 50px auto;
padding: 20px;
border: 1px solid #e2e8f0;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.add-goods {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
input {
flex: 1;
padding: 8px;
border: 1px solid #cbd5e0;
border-radius: 4px;
}
button {
padding: 8px 16px;
background-color: #4299e1;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s;
}
button:hover {
background-color: #3182ce;
}
.delete-btn {
background-color: #e53e3e;
}
.delete-btn:hover {
background-color: #c53030;
}
.goods-list {
list-style: none;
padding: 0;
margin: 0 0 20px 0;
border-top: 1px solid #edf2f7;
}
.goods-item {
padding: 10px;
border-bottom: 1px solid #edf2f7;
display: flex;
justify-content: space-between;
align-items: center;
}
.empty-cart {
text-align: center;
color: #a0aec0;
padding: 20px;
}
.total-price {
font-size: 18px;
font-weight: bold;
color: #2d3748;
}
.total-price span {
color: #e53e3e;
}
</style>
</head>
<body>
<div class="cart-container">
<h2>🛒 我的购物车</h2>
<!– 添加商品区域 –>
<div class="add-goods">
<input type="text" id="goodsName" placeholder="商品名称(如 牛奶)">
<input type="number" id="goodsPrice" placeholder="价格(如 3.5)" min="0" step="0.01">
<button id="addBtn">➕ 添加</button>
</div>

<!– 购物车商品列表 –>
<ul class="goods-list" id="goodsList"></ul>

<!– 总价展示 –>
<div class="total-price">
总价:¥ <span id="totalPrice">0.00</span>
</div>
</div>

<script>
// ========== 1. 获取页面元素 ==========
const goodsNameInput = document.getElementById('goodsName');
const goodsPriceInput = document.getElementById('goodsPrice');
const addBtn = document.getElementById('addBtn');
const goodsList = document.getElementById('goodsList');
const totalPriceSpan = document.getElementById('totalPrice');

// ========== 2. 数据存储(唯一数据源) ==========
let cartData = []; // 每个元素格式:{ name: '牛奶', price: 3.5 }

// ========== 3. 渲染购物车列表 ==========
function renderCart() {
// 清空列表,重新渲染(实际项目中可用虚拟DOM优化,这里简单处理)
goodsList.innerHTML = '';

// 如果购物车为空,显示提示
if (cartData.length === 0) {
const emptyLi = document.createElement('li');
emptyLi.className = 'empty-cart';
emptyLi.textContent = '🛍️ 购物车空空如也,快去添加商品吧~';
goodsList.appendChild(emptyLi);
return;
}

// 遍历数组,动态生成商品项
cartData.forEach((goods, index) => {
const li = document.createElement('li');
li.className = 'goods-item';
// 注意:这里用内联 onclick 传递 index 简单直观,但更推荐事件委托(见文末拓展)
li.innerHTML = `
<span>${goods.name} – ¥${goods.price.toFixed(2)}</span>
<button class="delete-btn" onclick="deleteGoods(${index})">删除</button>
`;
goodsList.appendChild(li);
});
}

// ========== 4. 计算总价 ==========
function calculateTotal() {
// 使用 reduce 累加价格,初始值0
const total = cartData.reduce((sum, goods) => sum + goods.price, 0);
// 保留两位小数展示(注意:浮点数精度问题可引入 toFixed 解决)
totalPriceSpan.textContent = total.toFixed(2);
}

// ========== 5. 添加商品 ==========
function addGoods() {
const name = goodsNameInput.value.trim();
const price = parseFloat(goodsPriceInput.value); // 转为浮点数

// 输入校验
if (!name) {
alert('请输入商品名称');
return;
}
if (isNaN(price) || price <= 0) {
alert('请输入有效的正数价格');
return;
}

// 添加到数据数组
cartData.push({ name, price });

// 清空输入框
goodsNameInput.value = '';
goodsPriceInput.value = '';

// 更新视图
renderCart();
calculateTotal();
}

// ========== 6. 删除商品 ==========
window.deleteGoods = function(index) {
// 从数组中删除指定索引的商品
cartData.splice(index, 1);

// 更新视图
renderCart();
calculateTotal();
}

// ========== 7. 绑定添加事件 ==========
addBtn.addEventListener('click', addGoods);

// 初始化渲染(空购物车提示)
renderCart();
calculateTotal();
</script>
</body>
</html>


二、核心代码深度解析(新手必看)

1. 数据驱动视图思想

整个购物车的核心是 数组 cartData。

  • 添加商品 → push 新对象

  • 删除商品 → splice 移除

  • 每次数据变化后,调用 renderCart() 和 calculateTotal() 重新渲染页面。

这种“数据变了,页面自动跟着变”的模式,正是现代前端框架(Vue/React)的核心原理。学会这个思想,你就迈出了进阶的第一步。

2. 输入校验的重要性

javascript

if (!name) { … }
if (isNaN(price) || price <= 0) { … }

  • 防止用户输入空字符串或负数,保证数据有效。

  • 使用 parseFloat 而不是 Number,因为 Number('') 会返回0,可能导致误判。parseFloat 对于空字符串返回 NaN,更安全。

3. toFixed(2) 与浮点数精度

商品价格可能是小数,直接累加会出现类似 0.1 + 0.2 = 0.30000000000000004 的问题。

  • 用 toFixed(2) 保留两位小数展示,解决了视觉问题。

  • 进阶方案:内部计算时可以将价格乘以100(转换为整数分),累加后再除以100,彻底避免精度误差。

4. 删除按钮的索引传递

html

<button οnclick="deleteGoods(${index})">删除</button>

  • 这种方式简单直观,适合小案例。

  • 潜在问题:如果商品列表很长,每个按钮都绑定独立的事件函数,可能影响性能;另外如果数组变化,索引可能错乱(这里每次重新渲染,索引正确)。

  • 优化建议:用事件委托(见文末拓展)。

5. reduce 求和

javascript

const total = cartData.reduce((sum, goods) => sum + goods.price, 0);

  • reduce 是数组求和最优雅的方法,第二个参数 0 是初始值。


三、常见问题与避坑指南

❓ 为什么价格输入使用 type="number" 还要校验?

number 输入框在用户输入字母时,value 会变成空字符串,parseFloat 得到 NaN,需要主动拦截。

❓ 删除后总价不更新怎么办?

检查是否在 deleteGoods 中调用了 calculateTotal(),以及 totalPriceSpan 的 textContent 是否正确赋值。

❓ 浮点数累加出现 0.30000000000000004 怎么办?

展示时用 toFixed(2) 解决。若需精确计算(如金融场景),可改用整数运算。


四、面试考点与进阶拓展

📌 面试题1:如何优化删除按钮的性能?

  • 答案:使用事件委托,在外层 ul 监听点击事件,通过 e.target 判断是否为删除按钮,再获取对应的商品索引(可以用自定义属性 data-index)。

📌 面试题2:如果商品数量成千上万,每次重新渲染全部列表是否影响性能?

  • 答案:可以用文档碎片(DocumentFragment)批量插入,减少回流。更高级的可以用虚拟DOM库。

📌 拓展方向(附代码思路)

  • 本地存储:在 addGoods 和 deleteGoods 中同步保存到 localStorage,页面刷新后读取。

    javascript

    // 保存
    localStorage.setItem('cart', JSON.stringify(cartData));
    // 读取
    cartData = JSON.parse(localStorage.getItem('cart')) || [];

  • 商品数量:增加数量输入框,数据结构改为 { name, price, quantity },总价按 price * quantity 累加。

  • 重复商品合并:添加时如果名称相同,增加数量而不是新建项。

  • 空购物车占位图:添加一张可爱的空车图片。


  • 五、总结

    通过这个购物车实战,你掌握了:

    • 如何用数组管理数据。

    • 如何动态渲染列表。

    • 如何绑定事件并操作数据。

    • 数据变化后如何更新视图。

    这个模式几乎是所有前端增删改查功能的雏形,以后遇到类似的需求(如待办事项、评论区、购物清单),你都能轻松应对。


    如果这篇文章对你有帮助,请点赞、收藏、评论,让更多初学者看到!
    有问题欢迎在评论区留言,我会第一时间回复。后续还会推出更多实战案例(如本地存储版购物车、带数量的购物车),敬请期待!

    赞(0)
    未经允许不得转载:171主机测评 » 前端必会实战|手把手带你写一个购物车(添加/删除/计算总价),附完整代码!
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址