TypeScript
react
scrollbar
virtual-scroll
scroll

better-scrollbar

better-scrollbar 适配 React/Vue,面向海量数据场景,提供虚拟滚动、自定义滚动条、动态尺寸计算、吸顶分组、底部锚定与长列表渲染能力。

better-scrollbar 最初是 React 自定义滚动条组件,现在更准确的定位是面向复杂数据界面的虚拟化滚动区域系统。当前仓库已经拆成 pnpm/Turborepo monorepo:底层是框架无关的 core,向上提供 React adapter、Vue 3 adapter,同时保留 better-scrollbar 兼容包,让旧用户还能沿用原来的导入路径。

项目定位

这个项目不应该只被描述成“更好看的滚动条”。更准确的说法是:一个可定制滚动容器,内置面向生产复杂场景的虚拟化能力。

  • 滚动区域:提供受控视口、自定义 track/thumb、原生/受控滚动模式,以及横向和纵向滚动状态。

  • 虚拟化内核:把逻辑 item index 映射到像素 offset,控制真实 DOM 数量,并支持动态高度测量后的修正。

  • 交互层:处理 sticky 分组、底部跟随输出、滚动锚点、方向感知 overscan、快速滚动时的 scroll-seek 占位。

  • 框架适配:React 通过组件和 render props 暴露能力,Vue 3 通过组件、slot 和 composable 暴露同一套核心语义。

核心设计

项目的核心是 @better-scrollbar/core 里的 virtual height index。

:: 框架无关的 block index,内部使用 Fenwick tree 记录动态高度差值。固定高度列表走直接数学计算,动态高度列表只维护被测量行的 delta,避免每次滚动扫描全部数据。

:: 测量反馈闭环。React/Vue adapter 先用 estimatedItemHeight 估算总高度,只渲染当前窗口;行挂载后再通过 ResizeObserver、mutation 检查和 offsetHeight 把真实高度写回索引。

:: 逻辑滚动与物理滚动分离。浏览器真实 scroll height 有上限,React adapter 会把超大逻辑滚动范围映射到安全的物理窗口,同时用逻辑 offset 驱动渲染区间。

better-scrollbar 虚拟滚动反馈闭环
flowchart LR
  Input["wheel / drag / scrollTo"] --> State["logical scroll state"]
  State --> Range["core virtual height index"]
  Range --> Window["rendered item window"]
  Window --> Measure["ResizeObserver + mutation checks"]
  Measure --> Range
  Range --> Bars["custom track + thumb"]

项目亮点

能力

设计选择

价值

大列表

itemCount + renderItem 索引渲染

逻辑上可以表达百万级甚至更大数据量,真实 DOM 数量仍然受控。

动态高度

默认 50,000 条的测量高度缓存

真实高度逐步修正估算模型,同时避免无限缓存。

sticky 分组

stickyIndicesgroupCounts

分组列表、日志段落、业务 section 可以在滚动中保持上下文。

流式输出

followOutput 与 scroll anchor

聊天、日志、Agent 输出可以自动贴底,同时保留用户主动向上滚动的意图。

快速滚动

scrollSeek 轻量占位

高速滚动时先渲染低成本占位,降低昂贵 row renderer 的压力。

定制视觉

React render props / Vue slots

产品可以替换 view、track、thumb,而不需要 fork 滚动逻辑。

包结构

  • @better-scrollbar/core:算法、共享类型、虚拟高度索引、overscan 工具、sticky 工具、浏览器 scroll height 安全处理。

  • @better-scrollbar/reactScrollBar 组件、ref API、render props、动态测量、可访问性元数据、sticky overlay 和自定义滚动条组装。

  • @better-scrollbar/vueBScrollBar、scoped slot、composable、暴露式 scroll 方法,并复用同一套 core index。

  • better-scrollbar:兼容包,继续导出 React adapter,降低旧用户迁移成本。

架构判断

项目最值得保留的边界是:核心数学不依赖 React 或 Vue;adapter 只负责生命周期、DOM 测量、事件、渲染和框架风格 API。

这个边界让项目的性能叙事更可信。range 计算、动态高度更新、sticky 计算、滚动窗口映射可以分别被测试;React/Vue 层则专注验证用户能看到的交互结果。

适合场景

  1. 大表格、日志、trace、时间线等需要穿越超大逻辑数据集的界面。

  2. 设计系统需要品牌化滚动条,但又不能放弃可访问性和可控滚动状态。

  3. 聊天、Agent 输出、构建日志这类需要“贴底但不打断用户阅读”的流式区域。

  4. 需要 sticky header、分组列表、动态高度行的复杂业务列表。

开源信号

  • TypeScript 优先,包导出、peer dependency、兼容包边界都比较清晰。

  • monorepo 把 core 算法和框架 adapter 拆开,便于后续扩展其他运行时。

  • 测试覆盖 virtual range、包导出、React 行为和 Vue 包形态。

  • README、API 文档和 demo site 可以共同支撑“虚拟化滚动区域”的产品叙事。

后续压力点

  • 站点表达应继续聚焦“virtualized scroll area”,不要退回“只是自定义滚动条”的窄定位。

  • 极限性能数据要继续用固定 benchmark contract 描述,避免把本地测量泛化成所有浏览器场景。

  • 首页首屏 API 应降低噪音,把第一学习路径收敛到 itemCountrenderItemestimatedItemHeightheightoverscan 和滚动条定制。