简短回答:**是的,你可以在不安装 App 的情况下为 Shopify 添加心愿单。**不,Shopify 并没有原生提供心愿单功能。而且,大多数 DIY 方法在真实店铺扩展到一定规模时都无法存活——但如果你的产品少于几百个,并且使用对开发者友好的主题,有几种方法是可以用的。
本指南为你提供每种可行方法、实际代码,以及对每种方法在哪里出问题的诚实评估。读完之后,你就能判断到底该上线 DIY 版本,还是干脆省去这些麻烦。
Shopify 内置心愿单功能吗?
没有。截至 2026 年,Shopify 在任何套餐中仍未包含心愿单功能——无论是 Basic 还是 Plus 都没有。你要么自己搭建一个,要么安装第三方 App。
这就是为什么这个关键词会存在。每个月有数万商家搜索 “shopify wishlist without app”,希望能找到一个隐藏设置。其实并没有。但是有四种合理的方法可以使用 Liquid、JavaScript 和 metafields 来搭建——让我们逐一了解。
方法一:localStorage 心愿单(最常见的 DIY)
这是 90% 的”免费心愿单”教程会教的方法。你在产品卡片上添加一个爱心按钮,将产品数据保存到浏览器的 localStorage,然后根据这个存储的数组渲染一个心愿单页面。
优点: 无需登录。可离线使用。零后端。约 50 行代码。
缺点: 心愿单只存在于同一个浏览器、同一台设备上。如果买家在手机上保存了商品,再用电脑回来,他们的心愿单就是空的。清除 cookies?没了。换浏览器?没了。你也无法在某件商品降价或补货时给他们发邮件——因为你完全不知道他们是谁。
第 1 步——在产品页面添加心愿单按钮
打开你的主题代码编辑器(Online Store → Themes → Edit code),找到你的产品模板 snippet(通常是 snippets/product-card.liquid 或位于 sections/main-product.liquid 内部)。把下面的代码放到你希望显示爱心的位置:
<button
type="button"
class="wishlist-btn"
data-id="{{ product.id }}"
data-handle="{{ product.handle }}"
data-title="{{ product.title | escape }}"
data-image="{{ product.featured_image | image_url: width: 400 }}"
data-price="{{ product.price | money }}"
aria-label="Add to wishlist">
<span class="wishlist-icon">♡</span>
<span class="wishlist-label">Save</span>
</button>
第 2 步——接入 JavaScript
将以下代码添加到 assets/wishlist.js(创建这个文件),并在 theme.liquid 中通过 {{ 'wishlist.js' | asset_url | script_tag }} 引入:
(function () {
const KEY = 'shopify_wishlist_v1';
const read = () => JSON.parse(localStorage.getItem(KEY) || '[]');
const write = (list) => localStorage.setItem(KEY, JSON.stringify(list));
function syncButton(btn, list) {
const inList = list.some(i => i.id === btn.dataset.id);
btn.classList.toggle('in-wishlist', inList);
btn.querySelector('.wishlist-icon').textContent = inList ? '♥' : '♡';
btn.querySelector('.wishlist-label').textContent = inList ? 'Saved' : 'Save';
}
function updateCount() {
const count = read().length;
document.querySelectorAll('.wishlist-count').forEach(el => {
el.textContent = count;
el.hidden = count === 0;
});
}
document.querySelectorAll('.wishlist-btn').forEach(btn => {
syncButton(btn, read());
btn.addEventListener('click', () => {
let list = read();
const exists = list.find(i => i.id === btn.dataset.id);
if (exists) {
list = list.filter(i => i.id !== btn.dataset.id);
} else {
list.push({
id: btn.dataset.id,
handle: btn.dataset.handle,
title: btn.dataset.title,
image: btn.dataset.image,
price: btn.dataset.price,
addedAt: Date.now()
});
}
write(list);
syncButton(btn, list);
updateCount();
});
});
updateCount();
})();
第 3 步——搭建心愿单页面
在 Online Store → Pages 中创建一个名为 “Wishlist” 的新页面,handle 设为 wishlist。然后创建一个模板 templates/page.wishlist.liquid 并将其分配给该页面:
{% layout 'theme' %}
<div class="page-width wishlist-page">
<h1>Your wishlist</h1>
<div id="wishlist-grid" class="wishlist-grid"></div>
<p id="wishlist-empty" hidden>
Your wishlist is empty.
<a href="{{ routes.collections_url }}/all">Browse products</a>.
</p>
</div>
<script>
(function () {
const list = JSON.parse(localStorage.getItem('shopify_wishlist_v1') || '[]');
const grid = document.getElementById('wishlist-grid');
const empty = document.getElementById('wishlist-empty');
if (!list.length) { empty.hidden = false; return; }
grid.innerHTML = list.map(item => `
<article class="wishlist-card">
<a href="/products/${item.handle}">
<img src="${item.image}" alt="${item.title}" loading="lazy">
<h3>${item.title}</h3>
<p>${item.price}</p>
</a>
<button data-id="${item.id}" class="wishlist-remove">Remove</button>
</article>
`).join('');
grid.addEventListener('click', e => {
const btn = e.target.closest('.wishlist-remove');
if (!btn) return;
const next = JSON.parse(localStorage.getItem('shopify_wishlist_v1'))
.filter(i => i.id !== btn.dataset.id);
localStorage.setItem('shopify_wishlist_v1', JSON.stringify(next));
btn.closest('.wishlist-card').remove();
if (!next.length) { grid.innerHTML = ''; empty.hidden = false; }
});
})();
</script>
这就是完整的 localStorage 心愿单。它能用。但同时,跨设备买家的流失率高达 30–50%,没有分析数据,并且当用户想要的商品打折或补货时你也无法给任何人发邮件。这就引出了方法二。
方法二:客户 metafields(“正规”的 DIY 方法)
如果你的买家会创建账户,你就可以通过 metafields 把心愿单存到客户对象上。这样心愿单就能跨设备跟随用户,理论上你也可以给他们发邮件——尽管发送这些邮件本身是另一个独立的问题。
优点: 跨设备同步。可查询的真实数据。能在清除浏览器数据后存活。
缺点: 需要客户登录(相比匿名访问,立刻产生 60–80% 的流失)。要从店面前端写入 metafields 需要使用带客户访问令牌的 Storefront API,或者搭配 App Proxy + 一个小型的 serverless 函数。这里没有复制粘贴的现成方案——你必须投入开发工作。
设置 metafield
进入 Settings → Custom data → Customers → Add definition:
- 命名空间和 key:
custom.wishlist - 类型:
JSON - 访问权限:Storefront 读取 + 写入
在 Liquid 中显示心愿单状态
{% if customer %}
{% assign wishlist_raw = customer.metafields.custom.wishlist.value | default: '[]' %}
{% assign wishlist = wishlist_raw | parse_json %}
<button
class="wishlist-btn-meta"
data-id="{{ product.id }}"
data-customer="{{ customer.id }}">
{% if wishlist contains product.id %}♥ Saved{% else %}♡ Save{% endif %}
</button>
{% else %}
<a href="{{ routes.account_login_url }}?return_url={{ request.path }}"
class="wishlist-btn-logged-out">♡ Log in to save</a>
{% endif %}
写入 metafield
这里事情就变得棘手了。Storefront API 不允许你从匿名 JS 直接修改 customer.metafields——你需要客户访问令牌,或一个经过身份验证的 App Proxy。一个最小化的 App Proxy 处理器(Node.js,部署到 Vercel/Cloudflare Workers)大致如下:
// /api/wishlist-toggle (Shopify App Proxy)
export default async function handler(req) {
const { customerId, productId } = await req.json();
const query = `
mutation updateWishlist($id: ID!, $value: String!) {
customerUpdate(input: {
id: $id,
metafields: [{
namespace: "custom",
key: "wishlist",
type: "json",
value: $value
}]
}) {
customer { id }
userErrors { message }
}
}
`;
// 1. Fetch current list
// 2. Toggle productId
// 3. Send mutation to Admin API
// 4. Return new state
}
当你处理完认证、错误状态和速率限制后,完整实现大约需要 200 行代码。对于以前没做过的开发者来说,实际构建时间为 1–2 天;对于做过的开发者来说,2–3 小时。
方法三:使用 line item properties 的隐藏购物车(稍后再买)
一些教程建议通过将心愿单商品以数量 0 或带有自定义属性(如 _wishlist: true)添加到购物车来”滥用”购物车功能。**别这么做。**这会破坏购物车的数学逻辑,扰乱结账流程,而且大多数主题仍会渲染这些商品。我把它列出来,仅仅是为了让你在 Reddit 上看到有人推荐时知道要避开。
唯一勉强说得过去的变体是:使用一个独立的 “Saved for later” 购物车属性,并在主购物车之外渲染它,比如这样:
<!-- in cart.liquid -->
{% assign saved = cart.attributes.saved_items | split: ',' %}
{% if saved.size > 0 %}
<div class="saved-for-later">
<h2>Saved for later</h2>
{% for handle in saved %}
{% assign p = all_products[handle] %}
<a href="/products/{{ p.handle }}">{{ p.title }} — {{ p.price | money }}</a>
{% endfor %}
</div>
{% endif %}
这只是个权宜之计。购物车属性在购物车被清空时会消失。略过吧。
方法四:可分享 URL 心愿单(社交化的小技巧)
这其实不是真正的心愿单——它是一个”分享你喜欢的商品”的链接。你在客户端构建一个列表,然后把它编码到一个 URL 中,让买家可以分享或加入书签。
// Build a share URL from the localStorage list
function getShareUrl() {
const ids = JSON.parse(localStorage.getItem('shopify_wishlist_v1') || '[]')
.map(i => i.id)
.join(',');
return `${location.origin}/pages/wishlist?items=${ids}`;
}
// On the wishlist page, hydrate from ?items=
const params = new URLSearchParams(location.search);
if (params.get('items')) {
const ids = params.get('items').split(',');
// Fetch each /products/{id}.js and render
Promise.all(ids.map(id =>
fetch(`/products/${id}.js`).then(r => r.json())
)).then(renderWishlist);
}
作为方法一的补充很有用——能让买家把列表分享给朋友或发给自己。但它本身不能算心愿单。
诚实的结论:什么时候 DIY 没问题,什么时候它会失败
如果你能对下面四个问题都回答是,那 DIY 就可以胜任:
- 你的店铺 SKU 少于约 500 个(这样手动维护还能保持理智)。
- 你不关心跨设备同步(或者你本来就强制要求客户登录账户)。
- 你不打算在心愿单商品打折或重新有货时给客户发邮件。
- 你有内部开发人员,或者你自己能轻松编辑 Liquid。
只要你想做下面任何一件事,DIY 立刻会崩溃:
- 给客户发邮件:“你心愿单里的商品已经补货”——你根本不知道 localStorage 这条记录属于谁。
- 通过短信或邮件挽回被遗弃的心愿单——同样的问题。
- 在分析中查看心愿单转化——你可以触发 GA4 事件,但如果不做后端工作,你无法将其与营收关联。
- 让访客无需创建账户就能保存商品,并且在他们以后登录时同步到他们的邮箱。
- 把它交给非技术背景的市场人员去管理。
这些不是理论上的限制。这是大多数从 DIY 心愿单起步的店铺在 6 个月内换成 App 的真实原因。
DIY 心愿单到底花多少钱(诚实点)
人们之所以搜索 “without an app”,是因为他们以为 App 才是贵的那个选项。让我们核算一下。
开发者构建 localStorage 版本:**2–4 小时。**按每小时 75 美元计算,那就是 150–300 美元,加上零持续成本。算合理。
开发者构建带邮件触发的 metafield 版本:**8–16 小时。**600–1,200 美元,再加上 Klaviyo 或类似工具来实际发送邮件(每月 20–45 美元)。还要算上每次主题更新或 Shopify 更改其 checkout extensions 时的维护成本。
一个独立的心愿单 App + 一个独立的补货通知 App + 一个独立的预购 App:每个通常 15–25 美元/月。所以这三项功能加起来是每月 45–75 美元。
而一个综合性 App:只有一笔账单。
或者:用一个 App 同时获得这三项功能,还带免费套餐
利益相关声明——我们是 Notify Me! 的开发者,我们做这个 App 是因为大多数店铺都同时需要心愿单 + 补货通知 + 预购这三项功能。客户收藏一个产品 → 它缺货 → 他们收到通知 → 如果一直不回货,你给他们提供预购选项。这三项功能是天然属于一起的。
实际配置过程是这样的
你可以从控制台逐页面(产品页、合集页、首页)开关功能——无需维护任何主题代码:
Notify Me! 中的心愿单设置页面——访客模式默认开启,并且一旦客户登录,心愿单会立刻关联到他们的账户。无需修改代码。
产品页按钮——完全可自定义
可选择爱心、书签或星形图标。两种状态的文本都可编辑。组件会内联渲染在你的 Add-to-Cart 按钮旁边:
产品页按钮设置——无需触碰 Liquid 即可设置前后两种状态的文本、图标和样式。
合集页组件——在每张卡片上都生效
DIY 方法最难搞定的地方:在每张合集页面卡片上放一个可用的保存按钮。App 用一个开关就搞定:
合集页面组件——选择图标、位置和显示样式。自动渲染在每张产品卡片上。
定价(按年计费)
| 套餐 | 价格 | 心愿单操作数 | 补货通知 | 预购订单 |
|---|---|---|---|---|
| Lite | 免费 | 终生 50 次 | 终生 100 次 | 终生 5 次 |
| Starter | $15.92/月 | 24,000/年 | 6,000/年 | 6,000/年 |
| Standard | $31.92/月 | 120,000/年 | 18,000/年 | 18,000/年 |
| Rocket | $55.92/月 | 300,000/年 | 60,000/年 | 120,000/年 |
| Plus | $399.92/月 | 无限 | 无限 | 无限 |
完整套餐详情请见定价页面。免费的 Lite 套餐之所以存在,是为了让你在付费之前能真正在你的店铺上测试它。大多数店铺会长期停留在 Starter 套餐——15.92 美元/月就能替代你本来要另外安装的三个 App。
上面这些 DIY 版本无法匹敌的功能:
- 访客模式(不强制登录)——心愿单在首次登录时自动关联到账户
- 每个客户可拥有多个命名列表
- 心愿单组件可放在首页、合集页和产品页上
- 在降价、低库存和补货时自动触发邮件和短信
- 内置预购按钮和部分付款
- 完整的 Liquid 自定义功能以匹配你的主题
- 原生支持 B2B 和 DTC
常见问题
能在不写代码的情况下为 Shopify 添加心愿单吗?
不安装 App 是做不到的。所有”无代码”方法要么需要安装心愿单 App,要么需要把代码粘贴到你的主题中。没有原生开关。
有免费的 Shopify 心愿单吗?
有,几个都有。上面提到的 localStorage 方法如果你自己搭建是免费的。App 方面,Notify Me 的 Lite 套餐免费提供终生最多 50 次心愿单操作,足够你在付费升级到更高套餐之前验证客户是否真的会在你的店铺使用这项功能。
Shopify 2.0 有心愿单吗?
没有。Shopify 2.0 是主题架构(sections、blocks、app blocks)——它并没有添加心愿单功能。但它确实让通过 app blocks 安装心愿单 App 变得更干净,这也是为什么大多数现代心愿单 App 都能在几秒内嵌入到 2.0 主题中。
我能只用 Liquid 实现心愿单吗?
部分可以。如果数据存放在客户 metafield 中,Liquid 可以显示心愿单,但 Liquid 无法从店面前端写入 metafields。你需要 JavaScript,并且需要 Storefront API 或 App Proxy 来保存项目。
怎么在不安装 App 的情况下给 Shopify 产品页添加心愿单?
使用上面的方法一:把心愿单按钮 snippet 粘贴到你的产品模板中,把 wishlist.js 添加到你的主题 assets,然后通过 templates/page.wishlist.liquid 创建一个心愿单页面。完整代码就在本指南中。
心愿单真的能提升转化率吗?
能——保存行为标志着真实的购买意向,使用心愿单的买家通常比仅浏览的买家有显著更高的转化率。更大的提升来自于在商品打折或补货时给心愿单持有者发邮件——这正是 DIY 方法力不能及的地方。
客户清除 cookies 时 localStorage 心愿单会怎样?
它会被永久删除。没有备份,无法恢复。这也是店铺逐渐放弃 DIY 方法的最大原因。
心愿单能触发补货通知邮件吗?
只有当你把心愿单和客户邮箱关联起来时才行。localStorage 做不到。客户 metafields 可以,但你仍然需要一个后端来检测库存变化并发送邮件。一个同时具备这两项功能的 App——比如 Notify Me!——能完全省去这些工作。
心愿单和等待列表有什么区别?
心愿单是买家想要记住的产品的精选列表;等待列表是针对暂时缺货或尚未发布的产品的队列。心愿单是开放式的;等待列表则受库存或发布日期的时间限制。大多数店铺最终两者都需要。
是用 Continue Selling When Out of Stock 好,还是用心愿单好?
这是不同的问题。Continue selling when out of stock 让你能接受无库存商品的订单(本质上就是预购)。心愿单则让买家可以保存商品而不必下单。如果你想要捕获对一个无货商品的需求,通常两者都需要——心愿单用来收集意向,预购选项给那些准备立刻购买的买家。
最后更新:2026 年 6 月。针对运行在 Online Store 2.0 上的 Shopify 主题编写。如果你的主题更老(vintage),Liquid 路径会略有不同——但 JavaScript 部分完全一致。
