关于 recently-viewed-products
这项技能帮助网店记录商品浏览并展示最近浏览组件,介绍各平台内置方案和自定义店面实现;展示商品时重新获取最新数据。
主要功能
- 01Shopify、WooCommerce 和 BigCommerce 方案
- 02使用浏览器存储浏览记录
- 03通过 localStorage 自定义跟踪
- 04从服务器刷新商品信息
- 05隐私同意与记录过期指导
适用场景
- 01在商品详情页添加最近浏览区块
- 02在购物车或首页显示浏览记录
- 03为无头店面构建浏览历史组件
- 04向个性化引擎提供浏览数据
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ finsilabs/ awesome-ecommerce-skills --skill recently-viewed-products安装后在 Claude Code 中输入/recently-viewed-products确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
tile.jsonevals/ssr-hydration-and-privacy-fallbacks/criteria.jsontask.mdevals/storage-utility-implementation/criteria.jsontask.mdevals/widget-component-and-batch-api/criteria.jsontask.md详细介绍
最近浏览的产品
概述
跟踪购物者浏览过的产品,并在产品页面、购物车和主页上通过“最近浏览”小组件展示这些产品。使用浏览器存储保留当前会话和跨会话的浏览记录。始终从服务器重新获取产品数据,避免显示过时价格或缺货商品。
何时使用此技能
- 为回访访客打造“接着上次继续浏览”的体验时
- 在产品详情页或购物车抽屉中添加“最近浏览”小组件时
- 与需要客户端行为数据的个性化引擎集成时
- 构建无头店面,并且需要不依赖后端的轻量级浏览记录时
核心说明
步骤 1:确定商家的平台并选择合适的方案
| 平台 | 推荐方案 | 原因 |
|---|---|---|
| Shopify | 在 OS2.0 主题中启用内置的 Recently Viewed 分区,或安装 Also Bought • Related products / LimeSpot Personalizer | Dawn 和大多数 OS2.0 主题都内置了原生的 Recently Viewed 分区;LimeSpot($15/月)提供 AI 驱动的个性化功能,包括跨会话的最近浏览记录 |
| WooCommerce | 使用 WooCommerce 内置的 Recently Viewed Products 小组件或简码,或安装 YITH WooCommerce Frequently Bought Together | WooCommerce 开箱即带有最近浏览小组件;可通过 外观 → 小组件 或 [recent_products] 简码放置 |
| BigCommerce | 使用 BigCommerce Page Builder 中的 Recently Viewed 小组件,或在 Cornerstone 主题设置中启用 | Cornerstone 和 BigCommerce Page Builder 都内置了原生 Recently Viewed 小组件,可自动使用浏览器 Cookie |
| Custom / Headless | 使用 localStorage 跟踪浏览记录,并通过服务端批量接口获取最新产品数据 | 客户端存储意味着无需服务器状态;展示时重新获取数据可确保价格和库存信息为最新 |
步骤 2:在平台上启用 Recently Viewed
Shopify
使用内置分区(OS2.0 主题 — Dawn、Sense、Craft):
- 前往 在线商店 → 主题 → 自定义
- 导航到产品页面模板
- 点击 添加分区 并搜索“Recently Viewed”——它会在分区列表中显示为 Recently viewed products
- 配置以下选项:
- 设置 要显示的产品数量上限(建议为 4–8 个)
- 设置分区标题文本(“Recently Viewed”“Your Browsing History”等)
- 设置产品卡片样式:是否显示价格、评分等
- 该分区会自动使用 JavaScript 和浏览器存储(localStorage),无需额外配置
在主页上显示:
- 前往主页模板
- 按照相同方式添加 Recently viewed products 分区
- 该分区只会为曾浏览过产品的回访访客呈现内容
LimeSpot Personalizer(跨设备、AI 驱动):
- 从 Shopify App Store 安装
- LimeSpot 在服务端跟踪浏览记录(而不仅是在浏览器中),因此最近浏览记录可跨设备和浏览器保留
- 在 LimeSpot 控制面板中配置 Recently Viewed 小组件的位置——通过应用分区可将其放在任何页面
WooCommerce
内置的最近浏览小工具:
- 前往 外观 → 小工具
- 找到 WooCommerce Recently Viewed Products 小工具
- 将其拖到 Shop Sidebar 或 Footer 小工具区域
- 配置:
- 要显示的商品数量:建议设为 4–6
- 显示标题:是/否
- 或者,直接在商品页面模板、侧边栏或 Gutenberg 区块中放置短代码
[woocommerce_recently_viewed_products per_page="4"]
适用于 Classic Editor / Gutenberg:
- 在 Gutenberg 中,添加一个 Shortcode 区块并粘贴
[woocommerce_recently_viewed_products per_page="4"] - 对于 Elementor:使用 Shortcode 小工具嵌入相同代码
WooCommerce 会将最近浏览的商品 ID 存储在访客的会话中(PHP 会话 / Cookie),无需安装插件。小工具会自动读取此会话。
BigCommerce
内置的最近浏览小工具(Cornerstone 主题):
- 前往 Storefront → My Themes → Customize
- 导航到商品页面模板
- 在侧边栏中,找到 Recently Viewed 区域并启用它(或在页面布局中调整其位置)
- 设置要显示的商品数量
- BigCommerce 使用浏览器 Cookie 跟踪最近浏览的商品,无需安装应用
使用 Page Builder:
- 前往 Storefront → Page Builder
- 打开要放置小工具的任意页面
- 将小工具面板中的 Recently Viewed Products 小工具拖到页面上
- 在右侧的小工具面板中配置显示设置
自定义 / Headless
基于 localStorage 的浏览记录跟踪:
// lib/recentlyViewed.js
const STORAGE_KEY = 'rv_products';
const MAX_ITEMS = 12;
const TTL_MS = 30 * 24 * 60 * 60 * 1000; // 30 days
export function recordView(productId) {
const items = getStoredItems();
const filtered = items.filter(i => i.id !== productId); // remove existing entry
const updated = [{ id: productId, viewedAt: Date.now() }, ...filtered].slice(0, MAX_ITEMS);
try { localStorage.setItem(STORAGE_KEY, JSON.stringify(updated)); } catch {}
}
export function getRecentlyViewedIds(excludeId = null) {
const now = Date.now();
return getStoredItems()
.filter(i => now - i.viewedAt < TTL_MS && i.id !== excludeId)
.map(i => i.id);
}
function getStoredItems() {
try { return JSON.parse(localStorage.getItem(STORAGE_KEY) ?? '[]'); }
catch { return []; }
}
在商品详情页记录浏览:
// In ProductDetailPage.jsx — read localStorage only in useEffect to avoid SSR mismatch
import { useEffect } from 'react';
import { recordView } from '../lib/recentlyViewed';
export function ProductDetailPage({ product }) {
useEffect(() => { recordView(product.id); }, [product.id]);
return (
<div>
{/* product content */}
<RecentlyViewedWidget excludeId={product.id} maxItems={4} />
</div>
);
}
小工具组件——从服务端重新获取最新商品数据:
// RecentlyViewedWidget.jsx
export function RecentlyViewedWidget({ excludeId, maxItems = 4 }) {
const [products, setProducts] = useState([]);
useEffect(() => {
const ids = getRecentlyViewedIds(excludeId).slice(0, maxItems);
if (ids.length === 0) return;
fetch('/api/products/by-ids', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ids }),
})
.then(r => r.json())
.then(data => {
// Preserve the viewed order (API may return in any order)
const map = new Map(data.products.map(p => [p.id, p]));
setProducts(ids.map(id => map.get(id)).filter(Boolean));
})
.catch(() => {}); // Non-critical — fail silently
}, [excludeId, maxItems]);
if (products.length === 0) return null;
return (
<section aria-label="Recently viewed">
<h2>Recently Viewed</h2>
<div className="recently-viewed-grid">
{products.map(product => (
<article key={product.id}>
<a href={product.url}>
<img src={product.image} alt={product.name} loading="lazy" width="150" height="150" />
<p>{product.name}</p>
<p>${product.price}</p>
</a>
</article>
))}
</div>
</section>
);
}
批量商品端点(Node.js):
// api/products/by-ids.js
export async function getProductsByIds(req, res) {
const { ids } = req.body;
if (!Array.isArray(ids) || ids.length === 0 || ids.length > 20)
return res.status(400).json({ error: 'Invalid ids' });
const products = await db.products.findMany({
where: { id: { in: ids }, published: true },
select: { id: true, name: true, price: true, image: true, url: true, inStock: true },
});
res.json({ products });
}
最佳实践
- 只存储商品 ID,不存储完整商品对象——价格和库存状态会变化;渲染小工具时始终从服务端重新获取数据
- 排除当前商品——传入
excludeId,避免显示购物者正在浏览的商品 - 静默失败——最近浏览小工具并非关键功能;捕获所有错误并不渲染任何内容,而不是导致页面出错
- 挂载后再填充数据——在 SSR 框架中,在
useEffect内读取localStorage,以避免服务端与客户端不匹配 - 尊重隐私同意——如果 Cookie 政策要求,只有在用户接受分析 Cookie 后才写入存储
- 考虑 TTL 过期——读取时清除超过 30 天的条目,避免显示已停售的商品
常见问题
| 问题 | 解决方案 |
|---|---|
| Next.js / SSR 中出现水合不匹配 | 只在 useEffect 内读取 localStorage,不要在渲染期间读取;将状态初始化为空数组 |
隐私浏览模式下 localStorage 抛出异常 | 使用 try/catch 包裹所有读写操作;当前会话改用内存数组 |
| 小工具显示缺货或已删除的商品 | 过滤服务端响应,仅包含 published: true 的商品;不要认为已存储的 ID 能反映当前商品目录状态 |
| 同一商品在多个位置重复出现 | 添加到列表前,先筛除该商品 ID 对应的现有条目 |
| 小工具加载时导致布局偏移 | 数据加载期间使用具有最小高度的骨架屏预留小工具高度,或将其放在首屏以下 |
相关技能
- @wishlist-save-for-later
- @product-page-design
- @product-comparison
- @storefront-theming
评论
支持 Markdown