
Element Plus Scrollbar 组件完全指南替换原生滚动条、手动控制与无限滚动【免费下载链接】element-plus A Vue.js 3 UI Library made by Element team项目地址: https://gitcode.com/GitHub_Trending/el/element-plusElement Plus 的Scrollbar滚动条组件用于替换浏览器原生滚动条提供跨平台一致的外观与交互同时保留原生滚动行为。它广泛服务于 el-table、el-select、el-cascader、el-tree 等组件内部也是业务中实现隐藏原生滚动条 自定义美观滚动条的通用方案。读完本文你将掌握其全部配置属性、事件与方法能实现固定高度滚动、横向滚动、按内容自适应高度、手动/编程式滚动控制以及基于end-reached事件的无限滚动加载。本文以仓库内文档 docs/en-US/component/scrollbar.md 为主体并结合组件源码与测试用例补充底层实现细节。基本用法用 height 固定滚动区域Scrollbar通过height属性设定滚动区域高度未设置时则按父容器高度自适应。文档示例basic-usage.vue如下template el-scrollbar height400px p v-foritem in 20 :keyitem classscrollbar-demo-item{{ item }}/p /el-scrollbar /template style scoped .scrollbar-demo-item { display: flex; align-items: center; justify-content: center; height: 50px; margin: 10px; text-align: center; border-radius: 4px; background: var(--el-color-primary-light-9); color: var(--el-color-primary); } /styleheight同时支持string与number两种类型如400px或400。从源码看该属性最终通过addUnit统一补全单位后应用到内部 wrap 容器上见 scrollbar.vue 中的wrapStyle计算逻辑const wrapStyle computedStyleValue(() { const style: CSSProperties {} const height addUnit(props.height) const maxHeight addUnit(props.maxHeight) if (height) style.height height if (maxHeight) style.maxHeight maxHeight return [props.wrapStyle, style] })值得注意即使未显式传入height只要父容器给出了确定高度并允许子元素滚动Scrollbar同样可以工作——它内部没有对高度做强制断言而是完全由实际渲染的 wrap 容器尺寸驱动滚动条计算。横向滚动当内容宽度超过滚动条宽度时会自动出现横向滚动条。文档示例horizontal-scroll.vue通过flex布局撑宽内容并配合flex-shrink: 0防止子项被压缩template el-scrollbar div classscrollbar-flex-content p v-foritem in 50 :keyitem classscrollbar-demo-item{{ item }}/p /div /el-scrollbar /template style scoped .scrollbar-flex-content { display: flex; width: fit-content; } .scrollbar-demo-item { flex-shrink: 0; display: flex; align-items: center; justify-content: center; width: 100px; height: 50px; margin: 10px; border-radius: 4px; background: var(--el-color-danger-light-9); color: var(--el-color-danger); } /stylewidth: fit-content让内容容器宽度随子元素自适应从而产生超出可视区的横向溢出。组件内部把横向与纵向滚动条分开渲染bar.vue同时渲染一个水平Thumb和一个垂直Thumb见 bar.vue各自独立计算位移与尺寸因此两个方向的滚动可以共存。最大高度与自适应收起max-height允许滚动条按需出现内容未超出最大高度时不显示滚动条超出后才出现。文档示例max-height.vue配合动态增删列表演示了这一行为template el-button clickaddAdd Item/el-button el-button clickonDeleteDelete Item/el-button el-scrollbar max-height400px p v-foritem in count :keyitem classscrollbar-demo-item{{ item }}/p /el-scrollbar /template script langts setup import { ref } from vue const count ref(3) const add () { count.value } const onDelete () { if (count.value 0) { count.value-- } } /script实现层面height/max-height的变化会触发专门的监听组件watch这两个属性在非native模式下于nextTick后调用update()重新测量内容并刷新滚动条见 scrollbar.vue。这是max-height模式下内容增减后滚动条尺寸/显示状态正确刷新的关键机制。手动控制滚动setScrollTop / setScrollLeft / scrollToScrollbar将滚动方法通过defineExpose暴露给父组件见 scrollbar.vue从而可以在任意时机编程式控制滚动位置。文档示例manual-scroll.vue用滑块驱动滚动template el-scrollbar refscrollbarRef height400px always scrollscroll div refinnerRef p v-foritem in 20 :keyitem classscrollbar-demo-item{{ item }}/p /div /el-scrollbar el-slider v-modelvalue :maxmax :format-tooltipformatTooltip inputinputSlider / /template script langts setup import { onMounted, ref } from vue import type { ScrollbarInstance } from element-plus const max ref(0) const value ref(0) const innerRef refHTMLDivElement() const scrollbarRef refScrollbarInstance() onMounted(() { max.value innerRef.value!.clientHeight - 380 }) const inputSlider (value: number) { scrollbarRef.value!.setScrollTop(value) } const scroll ({ scrollTop }: { scrollTop: number }) { value.value scrollTop } /script组件对外暴露的方法汇总如下方法说明签名setScrollTop设置滚动到顶部的距离垂直方向(scrollTop: number) voidsetScrollLeft设置滚动到左侧的距离水平方向(scrollLeft: number) voidscrollTo滚动到指定坐标支持两种重载(options: ScrollToOptions) void或(x: number, y: number) voidupdate手动更新滚动条状态如内容动态变化后重新测量() voidhandleScroll处理滚动事件内部方法也可手动触发以同步滚动条() voidwrapRef内部滚动 wrap 容器的 DOM 引用RefHTMLDivElementsetScrollTop/setScrollLeft对入参做了严格校验非数字时会通过debugWarn发出value must be a number的警告并直接返回避免产生无效赋值见 scrollbar.vue。scrollTo则直接透传给 wrap 容器的原生scrollTo支持ScrollToOptions与(x, y)两种调用形式scrollbar.vue。无限滚动end-reached 事件从 2.10.0 版本开始Scrollbar新增了end-reached事件当滚动到达末尾时触发可用于实现无限滚动触底加载更多。文档示例infinite-scroll.vue如下template el-scrollbar height400px end-reachedloadMore p v-foritem in num :keyitem classscrollbar-demo-item{{ item }}/p /el-scrollbar /template script langts setup import { ref } from vue import type { ScrollbarDirection } from element-plus const num ref(30) const loadMore (direction: ScrollbarDirection) { if (direction bottom) { num.value 5 } } /scriptend-reached的回调参数direction为top | bottom | left | right即到达的是哪个方向可按需决定加载策略如仅bottom时加载下一页。事件类型定义见 scrollbar.ts。distance触发距离阈值从 2.10.5 版本开始distance属性默认0用于设置距离边缘多少像素时提前触发end-reached。这在即将触底时预加载下一批数据的场景非常实用可以让加载过程对用户无感。distance大于0时handleScroll会基于scrollHeight - distance clientHeight scrollTop之类的判定提前报告到达见 scrollbar.vueconst arrivedStates { bottom: !isGreaterThan( wrapRef.value.scrollHeight - props.distance, wrapRef.value.clientHeight wrapScrollTop ), top: wrapScrollTop props.distance prevTop ! 0, right: !isGreaterThan( wrapRef.value.scrollWidth - props.distance, wrapRef.value.clientWidth wrapScrollLeft ) prevLeft ! wrapScrollLeft, left: wrapScrollLeft props.distance prevLeft ! 0, }组件内部还维护了distanceScrollState方向状态机通过DIRECTION_PAIRS在到达某端与离开对端之间做去重只有从非到达状态切入到达状态时才会触发一次end-reached从而避免在末尾反复滚动时重复触发scrollbar.vue。组件结构与渲染原理Scrollbar的模板结构非常清晰见 scrollbar.vuediv classel-scrollbar div classel-scrollbar__wrap tabindex... component :istag classel-scrollbar__view !-- slot -- /component /div template v-if!native bar :alwaysalways :min-sizeminSize / /template /divwrap真正发生滚动的容器类名el-scrollbar__wrap非native模式下会追加el-scrollbar__wrap--hidden-default类以隐藏原生滚动条scrollbar.vueview内容视图容器类名el-scrollbar__view其标签类型由tag属性决定默认div可改为ul、section等bar仅在非native模式下渲染的自定义滚动条内部再拆分为水平 / 垂直两个thumb滑块。组件通过provide(scrollbarContextKey)向bar提供scrollbarElement与wrapElement引用实现父子模块间通信scrollbar.vue。滚动条尺寸与位移的计算自定义滚动条滑块的长度和位移由 bar.vue 中的update计算const originalHeight offsetHeight ** 2 / wrap.scrollHeight const originalWidth offsetWidth ** 2 / wrap.scrollWidth const height Math.max(originalHeight, props.minSize) const width Math.max(originalWidth, props.minSize)滑块长度近似为可视区高度² / 内容总高度即内容越长滑块越短并受min-size默认 20px兜底防止内容过长时滑块过小难以点击。位移则在滚动时按(scrollTop * 100 / offsetHeight) * ratio换算为百分比 transformbar.vue。容器两端各留 2px 边距即 util.ts 中的GAP 4垂直/水平方向的关键属性名统一封装在BAR_MAPutil.ts中滑块样式由renderThumbStyle生成export const renderThumbStyle ({ move, size, bar }): CSSProperties ({ [bar.size]: size, transform: translate${bar.axis}(${move}%), })这些计算均有对应测试佐证。在 scrollbar.test.tsx 的垂直滚动测试中外层 204px、内层 500px 时滚动 100px断言滑块样式包含transform: translateY(50%); height: 80px;精确验证了上述公式水平方向测试同样断言translateX(50%); width: 80px;。响应式更新与 noresize 优化组件默认通过useResizeObserver同时监听 view 容器与 wrap 容器的尺寸变化并监听全局window resize事件任一变化都会调用update重算滚动条scrollbar.vue。noresize置为true时则停止所有这些监听——如果你的容器尺寸确定不变建议开启它以优化性能此时若内容仍会变化可手动调用暴露的update()刷新。此外组件还会监听 wrap 的transitionend与animationend事件来更新滚动条以覆盖 transform 驱动的过渡/动画场景如幻灯片切换中尺寸观测不到的问题scrollbar.vue。组件在onMounted、onUpdated时都会刷新滚动条并在onActivated配合KeepAlive时恢复之前记录的scrollTop/scrollLeftscrollbar.vue。API 参考以下 API 表完全继承自 docs/en-US/component/scrollbar.md并结合 scrollbar.ts 的源码补充了类型与默认值细节。Attributes名称说明类型默认值height滚动条高度string / number—max-height滚动条最大高度string / number—native是否使用原生滚动条样式booleanfalsewrap-stylewrap 容器的样式string / objectCSSProperties \| CSSProperties[] \| string[]—wrap-classwrap 容器的类名string—view-styleview 容器的样式string / object同上—view-classview 容器的类名string—noresize不响应容器尺寸变化若容器尺寸不变建议开启以优化性能booleanfalsetagview 容器的元素标签stringdivalways是否始终显示滚动条booleanfalsemin-size滚动条最小尺寸number20id2.4.0view 容器的 idstring—role2.4.0a11yview 容器的 rolestring—aria-label2.4.0a11yview 容器的 aria-labelstring—aria-orientation2.4.0a11yview 容器的 aria-orientationenumhorizontal \| vertical—tabindex2.8.3wrap 容器的 tabindexnumber / string—distance2.10.5触发end-reached的距离阈值pxnumber0其中wrap-style/view-style在源码中通过definePropTypeStyleValue([String, Object, Array, Boolean])定义因此除字符串外也支持 CSSProperties 对象与数组wrap-class/view-class同理支持类名字符串、数组与对象。ariaLabel/ariaOrientation经由useAriaProps注入scrollbar.ts。Events名称说明类型scroll滚动时触发返回滚动距离({ scrollLeft: number, scrollTop: number }) voidend-reached2.10.0滚动到末尾时触发(direction: top \| bottom \| left \| right) voidscroll事件在每次滚动时都会携带当前的scrollTop与scrollLeftscrollbar.vue可用于实现滚动监听 双向同步如前述手动滚动示例中的滑块回显。Slots名称说明default自定义滚动区域内容Exposes通过 ref 访问名称说明类型handleScroll处理滚动事件() voidscrollTo滚动到指定坐标(options: ScrollToOptions \| number, yCoord?: number) voidsetScrollTop设置滚动到顶部距离(scrollTop: number) voidsetScrollLeft设置滚动到左侧距离(scrollLeft: number) voidupdate手动更新滚动条状态() voidwrapRef滚动条 wrap 容器引用RefHTMLDivElement无障碍与键盘支持从 2.4.0 起组件补齐了无障碍相关属性role、aria-label、aria-orientation会透传到 view 容器上从 2.8.3 起tabindex可作用到 wrap 容器使滚动区域本身可被键盘聚焦。这意味着你可以将滚动区域标识为roleregion并给出aria-label描述让屏幕阅读器用户也能理解该区域的语义配合tabindex后用户可通过方向键在可滚动区域内聚焦并滚动符合现代可访问性实践。使用建议小结固定高度区域使用height如height400px滚动条出现与否由内容是否溢出自动决定自适应收起使用max-height内容少时无滚动条、内容多时自动出现横向内容内容宽度超过容器时自动出现水平滚动条配合flex与width: fit-content实现编程控制通过模板 ref 调用setScrollTop/setScrollLeft/scrollTo并在需要时调用update()强制刷新无限滚动监听end-reached并按需使用distance提前预加载注意事件的方向去重逻辑无需担心重复触发性能容器尺寸固定不变时设置noresize避免冗余的 ResizeObserver 与 resize 监听开销。如果你只需要纯原生的滚动条外观不追求跨浏览器统一的自定义样式将native设为true即可完全跳过自定义滚动条的渲染分支。【免费下载链接】element-plus A Vue.js 3 UI Library made by Element team项目地址: https://gitcode.com/GitHub_Trending/el/element-plus创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考