Dawn 商品推荐:相关商品、搭配商品与空结果行为
本文基于 Dawn v15.3.0,源码固定到 ecb06c3(核验于 2026-09-29)。结论限于此提交,不表示其他版本行为相同;本篇为静态源码分析,未完成店铺运行验证。Dawn 上游已发布 v16.0.0(2026-08-10,GitHub Releases 核对),本库 Dawn 正文统一以 v15.3.0 为基线,升级前需重新核对。
对应哪些通用模式
覆盖交叉销售与追加购买模块中「搭配」与「相关商品」两类推荐在 Dawn 商品页上的实现:相关商品用Grid,搭配商品用类似Carousel的分页滑块,单张商品卡是Cards。内容层的关系与依据规则见相关商品。升级、捆绑与追加购买不在本篇范围,见下文的未内置结论。商品页整体管线见 Dawn 架构,加购后的反馈见购物车篇。
入口与文件
| 文件 | 职责 / 何时输出 |
|---|---|
| sections/related-products.liquid | 独立分区:product-recommendations 容器、标题与商品网格;默认模板 product.json 把它放在商品页最后 |
| sections/main-product.liquid(complementary 区块) | complementary 区块(limit: 1,默认模板未包含):请求 intent=complementary,输出 aside 与分页滑块 |
| assets/global.js(ProductRecommendations) | product-recommendations 自定义元素:进入视口前加载、替换内容、搭配为空时自删 |
| snippets/card-product.liquid | 两处共用的商品卡;搭配区块可开启快速加购 |
| assets/component-complementary-products.css、assets/section-related-products.css、assets/component-slider.css | 搭配区块的样式;相关商品容器样式;滑块样式与减少动态设置 |
| templates/product.json | 默认区段顺序:main、image-with-text、multicolumn、related-products |
数据从哪来
- 推荐结果:两处都用
routes.product_recommendations_url加查询参数,由ProductRecommendations补上&product_id=<当前商品>§ion_id=<所在区段 id>。相关商品只带limit(products_to_show),搭配带limit(product_list_limit)与intent=complementary。端点的参数、intent默认值与返回,见交叉销售模块「映射到 Shopify」所引的官方页面,本篇未联网复核。 - Liquid 对象:模板里判断
recommendations.performed and recommendations.products_count > 0,循环recommendations.products。内容来自 JS 发起的请求:recommendations.performed预期只在经推荐端点渲染时成立(见交叉销售模块所引官方页面,未复核),因此普通页面渲染里这一分支预期不输出内容;响应是带section_id的分区渲染,JS 只取其中的product-recommendations元素(依据section_id的约定推断,未抓包)。 - 谁决定推荐哪些商品:主题不做任何选择,不筛选可售性,也不排除购物车内商品;区段里的说明文字让商家到 Search & Discovery 应用里管理。资格规则见交叉销售模块所引的帮助中心页面。
- 编辑器设置:
related-products:heading(默认「You may also like」)、heading_size、products_to_show(2 到 10,默认 4)、columns_desktop(1 到 6)、columns_mobile(1 或 2)、image_ratio、image_shape、show_secondary_image、show_vendor、show_rating、配色与边距。complementary:block_heading(默认「Pairs well with」)、make_collapsible_row、icon、product_list_limit(1 到 10,默认 10)、products_per_page(1 到 4,默认 3)、pagination_style(dots/counter/numbers,默认counter)、image_ratio(portrait/square)、enable_quick_add(默认关)。
交互怎样运行
加载。 connectedCallback 建立 IntersectionObserver(rootMargin: '0px 0px 400px 0px'),元素接近视口 400px 时才请求,请求一次后停止观察。响应解析进临时 div,取 product-recommendations,innerHTML 非空就替换当前元素内容;响应里有 .grid__item 时加 product-recommendations--loaded 类,检出内的 CSS 没有引用这个类(检索该类名)。请求失败只 console.error,没有检查 response.ok,也没有用户可见提示。
相关商品。 输出 h2 标题与 ul[role="list"] 网格,每项是 card-product;不传 quick_add,所以卡片上没有快速加购。列数由类名 grid--N-col-desktop、grid--N-col-tablet-down 控制,没有滑动或分页,swipe_on_mobile 这类设置在此分区里不存在。
搭配商品。 按 products_per_page 把结果分成若干「页」,每页是一个 role="group" 的纵向卡片列表,外层是 slideshow-component。多于一页才输出上一页、下一页按钮与计数器(点、n / N 或数字)。开启 enable_quick_add 后卡片传 quick_add: 'standard':单变体且无数量规则时卡片内直接是 product-form,否则是「选择选项」按钮并打开快速加购弹窗,两条路径最终都进入常规的加购与购物车反馈。可折叠形态用 details 包裹,图标沿用折叠行的图标集合。
换商品时。 选项值指向另一个商品 URL(data-product-url 不同)时,主区域整体被替换,新的 product-recommendations 重新连接并重新请求;同商品内的变体切换不会触发推荐刷新。ProductRecommendations 不订阅 cartUpdate,加购后推荐区不会自动刷新。
空结果与失败时
| 情形 | 相关商品 | 搭配商品 |
|---|---|---|
| 没有结果 | 标题与列表都不输出,容器保留(related-products 类与区段边距类仍在该元素上,实际留白未核验) | this.remove():响应里没有 slideshow-component 且元素带 complementary-products 类时整个元素被移除 |
| 请求失败 | 容器保持为空,仅控制台报错 | 元素保留为空,仅控制台报错 |
| 无 JavaScript | 只有空容器 | 只有空容器 |
与通用契约的一致与差距
| 通用要求(交叉销售、Carousel、Grid) | Dawn 现状 |
|---|---|
| 零项隐藏整个模块,不用随机商品填补 | 搭配已做(元素自删);相关商品只隐藏标题与列表,容器与边距类残留。主题本身不补随机商品 |
| 加载不阻塞主购买按钮,失败时安静隐藏 | 基本做到:视口前置加载、失败只报控制台;但没有为容器预留高度,加载完成时的布局位移未核验 |
| 推荐依据可解释、标题与依据一致 | 部分:标题是商家可改的固定文字,默认「You may also like」「Pairs well with」;每项没有理由字段,主题不知道结果来自手动还是自动 |
| 已在购物车、不可售、不兼容的商品不显示 | 主题不做筛选,完全依赖平台端点;加购后不刷新,刚加入的搭配商品会继续留在列表,直到换页或刷新(静态可推,未复现) |
| 限制数量,按类型分组 | 数量有上限设置(相关 2 到 10,搭配 1 到 10);两类推荐是两个独立区块,没有「升级」「捆绑」类型 |
| 加购按钮名称写明商品、不预勾选 | 快速加购按钮用 aria-labelledby 同时引用按钮与商品标题;没有任何预勾选控件;默认关闭快速加购 |
| 加购后所有购物车入口一致 | 复用常规 product-form 与购物车反馈,见购物车篇 |
| 搭配放在购买区之后 | 默认模板没有 complementary 区块;位置取决于商家在 main-product 内的区块顺序 |
| Carousel:真实按钮名称、当前序号与总数 | 已做:上一页、下一页是带 aria-label 的 button,默认计数器显示当前与总页数;只有一页时不输出控制 |
| Carousel:不自动轮播、暂停控制 | 搭配区块没有 data-autoplay,不自动轮播,也就没有暂停按钮 |
| Carousel:不可见项不进入 Tab 顺序 | 部分:setSlideVisibility 给非当前页加 aria-hidden="true"、tabindex="-1",但只处理其中的 a;页内的快速加购 button 未被处理(静态阅读,未验证) |
| Carousel:减少动态偏好 | 部分:component-slider.css 在 prefers-reduced-motion 下把 .slider 的 scroll-behavior 设为 auto |
| Grid:DOM 顺序即阅读顺序,用列表语义 | 已做:ul[role="list"] 与 li,按响应顺序输出 |
| Grid:一项时退化为单列 | 由 CSS 类控制,未逐项核验 |
| 状态变化不打断读屏 | 推荐区加载没有 live region,也不播报;这符合「不反复播报」,但读屏用户没有加载提示 |
在固定提交的全仓库搜索中,搭配商品由 main-product 的 complementary 区块内置(intent=complementary);以下均未找到:升级或捆绑商品的展示、「经常一起购买」勾选式加购、最近浏览、购物车页或抽屉里的推荐区块(搜索词:bundle、upsell、cross-sell、cross_sell、bought together、frequently、recently viewed、recently_viewed;intent 在整个检出内仅有 main-product.liquid 一处命中;范围为检出内全部文件,含 locales 与 config)。购物车页模板 cart.json 的末尾是 featured-collection,不是推荐。这只说明此版本主题没有内置,不表示平台不能实现。
定制入口与风险
商家的入口见「编辑器设置」;推荐结果本身在 Search & Discovery 应用里管理,主题内无法改。开发者的耦合点:
ProductRecommendations靠data-url、data-product-id、data-section-id与元素内的.grid__item、slideshow-component判断状态,改标记会破坏自删与--loaded逻辑。- 搭配请求使用
main-product的区段 id,响应是整段商品区段的渲染,只取其中的推荐元素;给该区段增加重内容会放大这次请求。 - 相关商品与搭配共用
card-product;卡片改动同时影响集合页、搜索与推荐。 ProductInfo里有一个relatedProducts取值器,在固定提交内没有调用点(检索relatedProducts),改推荐结构时不必为它兼容。- 想要推荐区随加购刷新或补上升级、捆绑,需要新增订阅与数据来源,主题内没有现成的扩展点。
建议的验证范围
本篇均未执行:
- 选一个有手动搭配、一个只有自动推荐、一个没有推荐的商品,核对两处区块的显示、隐藏与容器留白;观察加载前后是否有布局位移。
- 让推荐请求失败(离线或拦截),确认页面表现与控制台。
- 搭配区块设为多页、开启快速加购,键盘走完翻页、非当前页内的按钮是否仍可聚焦、加购到超过库存的反馈。
- 加购一个已被推荐的商品,确认推荐区是否仍显示它;再切换变体与换到组合商品,观察是否重新请求。
- 开启系统减少动态、关闭 JavaScript,核对滑块与空容器;用读屏软件听推荐区的标题与分页信息。