React 中面向你的商店的分类栏
分类栏——位于店铺商品网格上方、可点击的部门分类行——是电商场景中流量最高的滚动容器,而它们本质是菜单,不是轮播图:每个方块都是一个链接,没有吸附对齐,而边缘处露出半个方块正是在邀请用户继续滚动。
拖拽这一栏,或使用箭头——它们会在该行真正的端点处禁用。
为什么原生滚动在店铺页面上更胜一筹
店铺分类栏位于首屏之内,而首屏正是你为每一分 Lighthouse 分数拼尽全力的地方。轮播引擎需要用几十 KB 的手势模拟代码,去实现浏览器原生就能做到的事;本库压缩后(min+gzip)大约只有 ≈5.7 kB,并把滚动完全交给平台处理,因此没有 hydration 卡顿——这一栏在你的 JavaScript 加载完成之前就能滚动,也就意味着它在爬虫看到的服务端渲染 HTML 中同样可用。这个页面本身就是服务端渲染的证明:上方的演示在禁用 JavaScript 的情况下依然可以滚动。
对比页面 提供了与 Swiper、Embla、keen-slider 和 react-slick 的完整对比表格。
可见性跟踪是一项店铺场景功能
逐项可见性听起来像是一个实现细节,直到你把它对应到商品运营场景:
- 懒加载图片——在
useIsVisible报告该方块进入屏幕之前,先渲染一个占位方块。 - 曝光分析——
getVisible()(在首页的 首屏演示 中实时运行)能准确告诉你哪些分类被用户看到过,而不只是知道这一栏被渲染过。 - 边缘感知箭头——即便分类是异步加载进来的,也能在真正的端点处禁用或隐藏,如 新增项目示例 所示。
融入你自己的设计体系
方块是你自己的组件——图片卡片、圆形头像、文字胶囊——每一个都携带一个 itemId。高度和宽度由你的 CSS 决定;菜单本身不强加任何尺寸。可以借助 单项滚动 像商品滑块一样每次移动一项,也可以显示一个滚动 进度指示器,或者用 RTL 示例 为阿拉伯语、希伯来语商店提供 RTL 支持——这条分类栏是组合出来的,而不是配置出来的。
最简模式
带有 itemId 的方块,箭头来自可见性 hooks——整条分类栏的代码不到四十行。
function CategoryRail({ categories }: { categories: Category[] }) {
return (
<ScrollMenu LeftArrow={LeftArrow} RightArrow={RightArrow}>
{categories.map((category) => (
<CategoryTile itemId={category.id} key={category.id} {...category} />
))}
</ScrollMenu>
);
}
function CategoryTile({ itemId, name, image }: CategoryTileProps) {
// Lazy images for free: placeholders until the tile is on screen.
const visibility = React.useContext<publicApiType>(VisibilityContext);
const isVisible = visibility.useIsVisible(itemId, false);
return (
<a href={`/category/${itemId}`} className="rail-tile">
{isVisible ? <img src={image} alt="" /> : <div className="ph" />}
<span>{name}</span>
</a>
);
}
function LeftArrow() {
const visibility = React.useContext<publicApiType>(VisibilityContext);
const atStart = visibility.useLeftArrowVisible();
return (
<button disabled={atStart} onClick={() => visibility.scrollPrev()}>
←
</button>
);
}或者以 shadcn 组件的形式安装
基础的 scroll-menu 注册表条目就是这条分类栏——shadcn 样式的箭头、拖拽滚动、隐藏滚动条——安装进你的 components/ui/,并由你自己的 tokens 定义样式:
npx shadcn@latest add https://react-horizontal-scrolling-menu.dev/r/scroll-menu.json