事件让你可以在购物车中发生某事时运行代码。它们位于 window.aftersell.cart.events 下。
订阅是一个设置类调用,所以可以安全地放在脚本顶部,无需等待 ready()。
events.on(event, handler) 注册一个处理函数并返回一个用于取消订阅的函数:
events.once(event, handler):触发一次后自行取消订阅。
events.off(event, handler):移除特定的处理函数。
抛出错误的处理函数会被隔离并记录到控制台;其他处理函数仍会运行。
几乎所有事件相关的 bug 都可以追溯到这两条之一。
不要在没有防护的情况下从 cart_updated 更改购物车
在 cart_updated 处理函数内部更改购物车会再次触发 cart_updated。如果那个处理函数又更改了购物车,你就有了一个无限循环。购物者会看着购物车反复抖动,而页面则疯狂请求 Shopify。
**永远不要从 cart_updated 或 cart_loaded 中无条件地调用操作。**用一个针对你即将创建的状态的检查来防护它,使第二次执行什么都不做。
购物车确实给了你一道安全网:产生完全相同购物车的更新不会发出任何事件,所以没有任何变化的重新获取不会重启循环。这能保护你免受意外的空操作循环。但它不能保护你免受每次都真正更改购物车的处理函数的影响。
同一事件的所有处理函数接收的是同一个对象。修改它会改变你之后的处理函数看到的内容,包括商店中其他应用的处理函数。
要真正更改购物车,使用操作。要更改行的渲染方式,使用 registerLineTransform。
在购物车于页面上首次加载时触发一次。载荷是完整的购物车对象。
**适用场景:**任何需要针对购物车初始状态运行的逻辑,例如协调免费赠品、初始化小组件,或在页面加载时向分析工具上报购物车内容。
**cart_loaded 会向迟来的订阅者重放。**如果你在购物车已经加载后才订阅,你的处理函数会立即以当前购物车被调用。订阅顺序从不重要,所以你不必担心你的脚本是否抢在了购物车之前。
既要在页面加载时正确、又要在之后每次变化时正确的逻辑,应该用同一个函数同时订阅 cart_loaded 和 cart_updated。这是”让 X 与购物车保持同步”的标准模式。
在首次加载之后,购物车内容每次变化时触发,无论变化来自抽屉、你自己的操作、主题还是其他应用。载荷是完整的购物车对象。
**适用场景:**让购物车之外的东西保持同步,例如自定义总额、进度条、页眉徽章,或每次变化时的分析事件。
产生完全相同购物车的更新不会发出任何事件。重新打开抽屉、切换回标签页,或返回相同内容的重新获取都不会触发它。
在购物车中出现新的一行时触发。载荷是 { item },其中 item 是购物车行。
**适用场景:**在第三方分析工具中追踪加入购物车。这是 SDK 最常见的用途。参阅追踪加入购物车。
关于它的推导方式,有两点需要知道:
**数量变化不算添加。**购物车通过对行而不是数量进行差异比较来判断添加和移除。购物者把某行从 1 增加到 3 会触发 cart_updated,而不是 item_added。如果你也需要捕获数量增加,请在 cart_updated 处理函数中与之前的状态进行比较。
它也不会为页面加载时已经在购物车中的商品触发;那些通过 cart_loaded 到达。一次性添加多个不同产品会为每一行触发一次该事件。
在某行从购物车中消失时触发。载荷是 { item },即该行消失前一刻的样子,所以你仍然可以读取它的 key、variantId 和 title。
**适用场景:**撤销你在添加时做的事,例如清除标志、重新显示购物者拒绝过的优惠,或向分析工具上报移除。
与 item_added 有同样的注意事项:数量降低但未归零不算移除。
cart_opened 和 cart_closed
在抽屉打开和关闭时触发。没有载荷。
**适用场景:**浏览追踪、暂停抽屉后面的视频或轮播、切换页面上的类名。
两者都不会在页面初始加载时触发,只在实际打开或关闭时触发。
在购物者点击结账按钮时、浏览器跳转之前触发。没有载荷。
**适用场景:**结账意向追踪。
**你无法从这个处理函数中取消结账。**该事件是一个通知,而不是一道闸门;无论你的代码做什么,跳转都会发生。让处理函数保持快速和同步:await 或慢速网络调用可能在页面卸载前无法完成。任何需要可靠发送的内容请使用 navigator.sendBeacon。
每个事件也会作为 DOM CustomEvent 在 window 上派发,所以你可以在不接触 window.aftersell.cart 的情况下监听。这在主题文件、第三方应用或独立于购物车加载的脚本中很有用。
注意命名:总线使用 snake_case,DOM 事件在 aftersell:cart: 前缀后使用 kebab-case。
载荷通过 event.detail 到达,与购物车对象一致。事件在 window 上派发,所以页面上任何位置的监听器都能收到。购物车渲染在 shadow root 中,但 shadow 边界从不在事件的传播路径中。每次派发都会克隆载荷,所以修改 event.detail 的监听器不会影响其他任何人,抛出错误的监听器也不会干扰 SDK。
**cart-loaded 不会在 DOM 上重放。**总线会向迟来的订阅者重放 cart_loaded,但那条路径绕过了 DOM 派发,所以在购物车已加载后注册的 window.addEventListener('aftersell:cart:cart-loaded') 永远不会触发。如果你的脚本加载顺序无法保证,请使用会重放的 window.aftersell.cart.events.on('cart_loaded', …),或者同时监听 aftersell:cart:cart-updated。
另外,购物车在每次更改购物车时都会在 document 上发布 Shopify 的标准购物车事件,这样主题代码和其他应用就可以像响应主题的变更一样响应 Aftersell 的变更:
载荷不在 event.detail 上。detail 只携带 { source: 'aftersell' }——购物车用来忽略自己的事件以避免循环的标签。上表中的所有内容都直接赋值到事件对象上,所以要读取 event.action,而不是 event.detail.action。
每个事件还携带一个 promise,Aftersell 会在底层写入落定时将其 settle,符合 Shopify 的标准——await 它,不要去 resolve 它。这些事件在 document 上派发并会冒泡,所以 window 上的监听器也能收到。
- 购物车对象:上述载荷的完整结构。
- 操作:如何从处理函数中更改购物车。
- Hooks:用于更改购物车的渲染方式,而不是响应它。
- 使用案例:分析追踪、免费赠品和其他完整示例。