这个我们称为 Kestrel 的男士理容品牌,在自定义版块项目结束几周后发来了第二条消息。里面只有一张截图:一家珠宝店的商品页,加入购物车按钮下方有一排卡片,写着买 1 送 1,选择你的第二件。下面一句很短的问题:我们的商品页也能这样吗?
Shopify 买一送一的优惠在后台很容易开启,难的是让顾客真正用起来。本案例介绍我们如何构建这个选择器,为什么没有照搬参考店铺的代码,以及让第二件商品真正免费的那一个 Shopify 设置。
关于本案例:这是根据真实 Shopify 项目整理的综合案例。品牌名称、商品照片和部分细节已更改,商品图片为示意插图。代码模式、遇到的问题和解决方法与我们实际处理的完全一致。

项目一览
| 客户 | 直面消费者的男士理容品牌,荷兰(荷兰语店铺,欧元) |
| 平台 | Shopify Online Store 2.0,保留现有的高级主题 |
| 范围 | 1 个 snippet、1 个样式表、1 个脚本、1 个新的商品页区块,不使用应用 |
| 折扣 | Shopify 原生自动折扣(买 X 得 Y) |
| 交付方式 | 商家自己在主题代码编辑器中逐个文件安装 |
为什么只有原生 BOGO 折扣还不够
Shopify 支持“买 X 得 Y”折扣已有多年。设置好之后,只要购物车里有符合条件的商品,折扣就会生效。问题就出在“只要”两个字上。顾客得先知道有这个优惠,离开商品页,找到符合条件的商品,加入购物车,再回来。大多数人不会这样做。只写着“1+1 免费”却无法直接操作的横幅,带来的主要是询问“折扣为什么没生效”的邮件。
参考店铺用一个选择器解决了这个问题:符合条件的商品就显示在做购买决定的位置,点一下即可选择,主按钮随之变成将 2 件商品加入购物车。这正是 Kestrel 想要的体验。
读懂参考,然后放到一边
商家还附上了参考店铺的 CSS、JavaScript 和 HTML。那是很用心的作品:大约 400 行,包括底部弹层、“查看全部”浮层、商品照片背景色检测,以及自己的加购请求。但它是为另一个主题、另一种表单和另一个购物车抽屉写的,而且属于别人。
所以我们只把它当作行为说明,而不是代码。动手之前,我们先读了 Kestrel 主题里的商品表单。这成了整个项目最有价值的一小时。
发现:主题本来就会一次加入两件商品
主题的 product-form 元素自带“经常一起购买”复选框功能。提交时,它会收集与表单关联的元素中所有已勾选的 input[name="bundles"],与主商品一起发送到 /cart/add.js。它还负责购物车抽屉、购物车页面模式、错误提示和固定的加购按钮。
这意味着选择器根本不需要自己的购物车请求。每张卡片里有一个名为 bundles 的隐藏复选框,选择器元素带上表单的 id:
<bogo-picker form="{{ product_form_id }}">
<label class="bogo-card__btn">
<input type="checkbox" name="bundles" value="{{ variant.id }}" class="sr-only">
<span>Choose</span>
</label>
</bogo-picker>
选中一张卡片,它的复选框就被勾选。按下主按钮,主题会用一个请求加入两件商品,打开自己的购物车抽屉并更新数量,和其他商品完全一样。我们写的 JavaScript 大约一百行,没有一行碰到购物车。

让它看起来像原生功能的细节
- 一次只能选一件赠品。选中一张卡片会取消其他卡片,顾客不会误加三件赠品。
- 按钮说清楚会发生什么。选中卡片后,按钮文字变为“将 2 件商品加入购物车”。主题在切换款式时会重置这段文字,所以一个小小的
MutationObserver会把它改回来。 - 卡片上可选款式。有多个款式的商品会显示一个紧凑的下拉框,复选框的值随之变化。
- 卡片对齐。卡片通过 CSS subgrid 共享网格行,即使某个商品名换成两行,名称、价格和按钮也都对齐。
- 移动端优先。手机上可见 2.3 张卡片,下一张从边缘露出;桌面端显示三张卡片并带箭头按钮。
- 干净地重置。加购成功后,选择器监听主题的
cartUpdate事件并清除选择。
这个区块可以像其他商品页区块一样在主题编辑器中添加。商家选择一个包含可赠商品的系列,或者用商品列表元字段为不同商品设置专属优惠。“免费”“选择”和按钮文字等都是设置项。当前商品和售罄商品会自动排除。
让第二件商品真正免费
选择器只负责把两件商品放进购物车。价格由 Shopify 通过自动折扣处理:
- 创建一个包含可赠送商品的系列,例如“BOGO 免费”。
- 在折扣中创建一个买 X 得 Y折扣,方式选择自动。
- 顾客购买:从包含所有付费商品的系列中最少购买 1 件。
- 顾客获得:从“BOGO 免费”中获得 1 件,价值设为免费。
- 勾选设置每个订单的最大使用次数并填写 1,否则买三件就会送三件。

Shopify 在“顾客购买”字段里没有“所有商品”选项,所以这里需要一个系列。一个条件为“价格大于 0”的自动系列会自己保持更新。有一个特殊情况值得了解:如果赠品也在这个系列里,从“BOGO 免费”中买两件就会有一件免费。对大多数店铺来说这没问题。如果不行,就给 BOGO 商品加标签,并在系列条件中排除该标签。

不上传主题也能安装
Kestrel 的线上主题里还有其他未发布的改动,所以上传新主题版本不可行。我们把功能拆成五个精确的步骤交给代码编辑器:三个新文件(一个 snippet、一个样式表和一个脚本),以及商品版块中两处小改动,一行 render 和一条 schema 区块定义。每一步都写明要搜索哪一行、在它上方粘贴什么。因为所有改动都是新增的,下次主题更新时只需几分钟即可重新应用。
我们还为“BOGO 免费”系列制作了一张简单的缩略图,让它在主题展示的任何地方都显得完整。
给考虑做 BOGO 的店铺的建议
- 把选择放在做决定的地方。需要顾客自己拼凑的优惠,大多数人会直接跳过。
- 写代码前先读懂你的主题。这个项目里最好的一行代码,是我们不必写的那一行,因为主题本来就能把组合商品加入购物车。
- 把价格逻辑留在 Shopify。原生自动折扣在购物车、结账和所有报表中都有效,而且可以随时暂停,不用改动主题。
- 从第一天起就限制每个订单的使用次数。
如果你也想在店铺中加入买一送一选择器、组合套装构建器或其他定制的商品页功能,请了解我们的 Shopify 主题开发服务,或告诉我们你的店铺情况。
常见问题
Shopify 不用应用能做买一送一吗?
可以。“买 X 得 Y”自动折扣是 Shopify 内置的。Shopify 没有的是让顾客在商品页上挑选赠品的方式,而这正是选择器补上的部分。
选择器能用于我的主题吗?
这个思路适用于任何 Online Store 2.0 主题。具体实现取决于你的主题商品表单如何加购。这里我们复用了主题的组合商品功能;在其他主题中,我们会用一次 Cart API 请求加入两件商品。
赠品可以有多个款式吗?
可以。有多个款式的商品会在卡片上显示一个小下拉框,加入购物车的就是所选款式。
顾客会滥用这个优惠吗?
决定什么免费的是折扣,而不是选择器。把每个订单的使用次数上限设为 1,一个订单最多只能获得一件赠品。
不同商品可以显示不同的赠品吗?
可以。区块默认读取一个系列,也可以用商品列表元字段按商品单独覆盖。