先判断问题出现在哪里
传统的 Shopify 购物车体验是:点击加购 -> 页面白屏跳转到 /cart 页面 -> 如果想继续购物,点击浏览器的后退按钮。这种充满摩擦的流程会扼杀用户的连带购买欲。

核心逻辑是:通过 Shopify Storefront Cart API 实现无刷新数据交互。
利用 JavaScript 的 fetch API,在后台默默地向 Shopify 发送加购、更新数量、删除商品的请求。拿到返回的最新购物车 JSON 数据后,使用 DOM 操作或现代前端框架(如 Vue/Alpine.js)局部更新页面右上角的购物车数量角标,并滑出一个精美的抽屉式购物车(Drawer Cart)。
实战步骤
步骤 1:拦截默认的表单提交 (Prevent Default)
操作路径:在 product-form.js 中编写监听逻辑
- 找到产品页的加购表单(
<form action="/cart/add">)。 - 监听
submit事件,并阻止其默认的跳转行为:form.addEventListener('submit', async (e) => { e.preventDefault(); const formData = new FormData(form); // 执行 Ajax 加购逻辑... });
步骤 2:调用 Cart API 添加商品
操作路径:使用 fetch 发送 POST 请求
- Shopify 提供了非常简单的 Ajax API 接口。
fetch(window.Shopify.routes.root + 'cart/add.js', { method: 'POST', headers: { 'X-Requested-With': 'XMLHttpRequest' }, body: formData }) .then(response => response.json()) .then(item => { console.log('加购成功:', item.title); // 触发打开抽屉购物车的事件 openCartDrawer(); // 刷新购物车数据 refreshCart(); }) .catch(error => console.error('加购失败:', error));
步骤 3:获取并渲染最新的购物车数据
操作路径:调用 GET /cart.js 接口
- 加购成功后,需要获取购物车里的所有商品来渲染抽屉。
fetch(window.Shopify.routes.root + 'cart.js') .then(response => response.json()) .then(cart => { // cart.item_count 是总数量,更新右上角角标 document.querySelector('.cart-count').textContent = cart.item_count; // 遍历 cart.items 数组,拼接 HTML 字符串,渲染到抽屉的列表中 renderCartItems(cart.items); // 更新底部总价 document.querySelector('.cart-total-price').textContent = formatMoney(cart.total_price); });
步骤 4:在抽屉内实现数量修改与删除
操作路径:调用 POST /cart/change.js 接口
- 在抽屉里,每个商品旁边都有
+和-按钮。 - 当用户点击
+时,获取该商品的key(购物车中该行的唯一标识)和新的数量。 - 发送请求更新数量:
fetch(window.Shopify.routes.root + 'cart/change.js', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: itemKey, quantity: newQuantity }) }) .then(...) // 重新调用 refreshCart() 刷新整个抽屉 - 删除商品就是将
quantity设为0。
常见误区与处理方法
误区一:使用纯 JS 拼接复杂的 HTML 导致代码难以维护 (XSS 风险)
规避方法:在上面的步骤 3 中,如果你用原生的 JavaScript 去遍历 cart.items,然后写出像 html += '<div class="item"><h3>' + item.title + '</h3></div>' 这样的代码,这不仅非常痛苦、难以维护,还存在严重的 XSS 跨站脚本攻击风险。现代 Shopify 主题开发,强烈建议使用 Section Rendering API 来代替纯 JS 拼接。 你可以请求 /?section_id=cart-drawer,让 Shopify 服务器直接用 Liquid 渲染好抽屉的 HTML 代码返回给你,前端只需做一次 innerHTML 替换即可。这既保证了代码的整洁,又理想复用了 Liquid 的各种过滤器(如价格格式化、图片裁剪)。
误区二:忽略了 Ajax 请求的并发冲突 (Race Conditions)
规避方法:用户在抽屉里反复点击 + 按钮,一秒钟内点了 5 次。由于网络延迟,这 5 个 Ajax 请求到达服务器和返回的顺序可能是错乱的。最终购物车里显示的数量和总价会完全对不上。在处理购物车数量更新时,需要加入防抖(Debounce)机制或请求锁定(Loading State)。 当用户点击 + 后,立刻将整个抽屉加上半透明的 Loading 遮罩,并禁用所有按钮,直到收到服务器的成功响应后,再解除锁定。确保同一时间只有一个购物车修改请求在处理。
误区三:多语言/多货币环境下的路由前缀丢失
规避方法:在写 fetch 请求时,很多新手习惯直接写 fetch('/cart/add.js')。这在单语言店铺没问题。但如果店铺开启了 Shopify Markets,用户切换到了法语版(URL 变成了 yourstore.com/fr)。此时你再请求 /cart/add.js,会报 404 错误或者丢失语言上下文。永远、永远不要硬编码绝对路径! 需要使用 Shopify 注入到全局的路由变量:fetch(window.Shopify.routes.root + 'cart/add.js')。window.Shopify.routes.root 会自动带上当前的语言前缀(如 /fr/),确保 Ajax 请求在任何国际化环境下都能精准命中正确的接口。
常见问题
学习「Shopify Ajax 购物车开发:手写抽屉式购物车 (Drawer Cart)」前需要什么基础?
建议先熟悉 HTML、CSS、基础 JavaScript 和 Shopify 后台结构。涉及 Liquid、Section、Schema 或主题工作流的内容,可以边读边在测试主题里练习,不要直接改线上主题。
可以直接在正在使用的线上主题里操作吗?
不建议。主题开发和结构调整应先在复制主题、开发主题或本地环境中完成,确认移动端、产品页、购物车和关键模板正常后,再发布到线上主题。
修改主题前最应该备份什么?
至少保留当前主题副本,并用 Git 记录代码变化。如果文章涉及主题编辑器配置,还要注意模板 JSON 和 settings_data.json 这类配置文件是否需要同步。
遇到教程和后台界面不一致怎么办?
优先以当前 Shopify 后台、主题代码和官方文档为准。Shopify 后台和 CLI 会持续更新,旧截图可用于理解路径,但不能替代当前界面提示。
这类主题开发内容适合什么时候上线到正式店铺?
当改动已经在测试主题中完成移动端、桌面端、产品页、集合页、购物车和速度检查后,再安排上线。影响结账、价格、库存或应用兼容的改动要单独回归。