Shopify 知识库 · 指南
Horizon 商品推荐:分区请求、intent 与空结果回退
基于 Horizon 4.2.0 源码,说明商品页推荐区块如何用分区渲染接口按 related 或 complementary 取回商品、怎样为空时回退或隐藏,以及与商品卡、轮播的关系,并对照交叉销售模块列出差距。
历史资料
请结合文中的适用版本和来源阅读。
本文基于 Horizon 4.2.0,源码固定到 5acd1b6(核验于 2026-10-07)。结论限于此提交,不表示其他版本行为相同;本篇为静态源码分析,未完成店铺运行验证。
交叉销售与追加购买一文写明「本站对 Horizon 的解读未涉及推荐区块」,本篇补上。卡片本身与快速购买见商品卡。
对应哪些通用模式
- 模块:交叉销售与追加购买中的「取推荐并展示」。本主题的推荐区块只负责展示,不含捆绑、结账后追加,也不提供预勾选或一起购买的组合加购。
- 展现:
layout_type为 carousel 或开启carousel_on_mobile时用轮播,否则为网格。
入口与文件
| 文件 | 职责 | 何时输出 |
|---|---|---|
| sections/product-recommendations.liquid | 独立分区,含骨架、结果、回退分支 | templates/product.json 在 main 之后放了一个,recommendation_type 为 related |
| blocks/product-recommendations.liquid | 同一逻辑的区块版,商品取自 closest.product | _product-details 的预设带一个 complementary 区块,标题默认「搭配造型」 |
| assets/product-recommendations.js | <product-recommendations>:懒加载、fetch、缓存、hydration morph | 任一版本输出后 |
| snippets/product-recommendations-styles.liquid | 骨架样式;data-has-recommendations='false' 时整体隐藏 | 两个版本 |
| snippets/resource-list-carousel.liquid | 轮播模式,转交 slideshow 片段 | layout_type 为 carousel 或移动端轮播 |
数据从哪来
- 请求:
data-url为routes.product_recommendations_url加?limit=N,脚本再追加&product_id=…§ion_id=…&intent=…。分区版的商品为section.settings.product.id | default: product.id(product.json把该设置绑定为动态来源closest.product);区块版为closest.product.id。section_id是宿主分区,区块版下,按 Section Rendering API 的语义响应是整个宿主分区的 HTML,客户端只合并带 key 的子树。 - intent:只来自设置
recommendation_type,取related或complementary,schema 默认related;后台文案content.complementary_products写明 complementary 需在 Search & Discovery 中设置。 - 对象:响应里的 Liquid
recommendations.performed、products、products_count;初始 HTML 中performed为假,输出骨架。 - 卡片:
content_for 'block'复用_product-card。分区版传product_view_context: 'recommendation',区块版未传。 - 设置:分区
product、layout_type、carousel_on_mobile(分区默认 false,区块默认 true)、max_products(分区 3–10,区块 2–10,默认 4)、columns、mobile_columns、icons_style、icons_shape。 - 搜索范围:全仓库搜索
recommendation,非 locales 的命中只有上述文件、templates/product.json、blocks/_product-details.liquid、snippets/resource-list-styles.liquid(注释)、variant-picker.js(注释)、standard-events.d.ts与与本题无关的README.md措辞;搜索productRecommendations、graphql无命中。购物车、集合、首页文件中未找到推荐区块或对该接口的调用。
交互怎样运行
- 懒加载:
IntersectionObserver(rootMargin: 0 0 400px 0)进入后断开并加载;MutationObserver监听自身属性,除data-error、hidden类与performed置真外的变化都会重新加载;performed已为true时直接返回。 - 请求:结果按完整 URL 缓存在实例内存;新请求前 abort 上一个,
fetch(url, { signal })真正取消;!response.ok视为失败。 - 渲染:正文非空则置
performed='true',调用morphSection的hydration模式(并注入分区样式表),只更新已存在且data-hydration-key相同的目标。 - 失败:
#handleError写console.error、加hidden类与data-error,界面无提示;设计模式下忽略!ok。AbortError也进同一个catch,重复触发时可能先隐藏再合并,静态推断,未核验。 - 服务端为空(
performed且products_count == 0):related回退为collections.all.products | reject: 'id', 当前商品的前max_products件;complementary置空,data-has-recommendations="false",CSSproduct-recommendations:has([data-has-recommendations='false'])整体display:none。 - 布局:网格用
resource-list--grid;轮播用slideshow(infinite: false、无 autoplay、箭头可选、未传位置控件),slide_count取recommendations.products.size,回退商品时为 0,视觉影响未核验。carousel_on_mobile会同时输出网格与移动轮播两份,靠hidden--mobile、hidden--desktop切换。未选商品时输出占位卡片(onboarding 分支)。 - 无 JS:只有骨架,不会出现推荐。
与通用契约的一致与差距
对照交叉销售与追加购买。
| 契约 | Horizon 4.2.0 | 判断 |
|---|---|---|
| 零项时隐藏,不用随机商品填补 | complementary 为零时隐藏;related 为零时用全部商品集合回填并显示 | related 与契约相反 |
| 区分搭配与类似 | recommendation_type 区分 intent;标题是自由文本块,product.json 中为「You may also like」 | 部分,依据与理由无字段 |
| 商品可售、排除购物车已有项 | 交给平台;回退分支只排除当前商品,未过滤可售 | 回退分支缺口 |
| 加载预留空间、失败安静隐藏 | 骨架加 400px 预加载,失败加 hidden;骨架 aria-label 写在无角色的 div 上,读出与否未核验 | 一致,读屏未核验 |
| 请求不阻塞购买按钮 | 进入视口下方 400px 才加载;分区版脚本带 fetchpriority="low",区块版没有 | 一致 |
| 购物车内放 1 至 3 项 | 内置推荐只在商品页语境,购物车文件中未找到 | 无内置 |
| 加购一致、无预勾选、拒绝路径 | 复用 _product-card;无预勾选与组合加购 | 不涉及 |
| 跨市场重算、不长期缓存库存 | 服务端按当前请求渲染;缓存仅在页面实例内存 | 跨市场未核验 |
| 埋点与视图上下文 | 分区版带 recommendation,区块版未带 | 不一致 |
| Carousel:名称、状态、键盘、可暂停 | 箭头按钮有本地化名称(accessibility.slideshow_previous、slideshow_next);无 autoplay,故无暂停问题;无位置指示;幻灯片靠 aria-hidden 随可见性切换,键盘行为未核验 | 部分 |
定制入口与风险
- 编辑器:上文设置项;分区自身可放
text、image、button、group等区块作标题区。 - 耦合点:
data-hydration-key、data-section-id与宿主分区 ID 是 hydration 的匹配条件;product.json里product设置依赖closest.product;改_product-card会同时影响集合、搜索与推荐。 - 回退是产品决策:
related用全部商品回填与契约相反,若要「零项隐藏」,需要改sections与blocks两处分支。
建议的验证范围
本篇未执行。1) 抓取商品页推荐请求,核对 product_id、section_id、intent、limit;2) 用有、无相关商品的商品测 related,确认零项回填;3) 在 Search & Discovery 配置 complementary,再测有、无配置两种情形;4) 模拟 500,确认区块隐藏且无报错提示;5) 切换轮播与移动端轮播,核对卡片重复输出与键盘、读屏;6) 更换市场与语言,核对推荐与价格。