从零打造一个丝滑的 Vue 3 返回顶部组件
这个组件具备以下特性:
- 智能显示:滚动超过指定距离(默认 300px)后自动出现。
- 丝滑动画:使用 Vue 内置的
<Transition>实现淡入上滑的出现 / 消失效果。 - 平滑滚动:点击后通过
behavior: 'smooth'实现平滑回顶。 - 类型安全:使用 TypeScript 定义 Props,开发体验极佳。
它涵盖了 Vue 3 开发中几个非常核心的最佳实践:
- Composition API 的逻辑复用与组织。
- 生命周期钩子 的正确使用(特别是事件解绑)。
- 原生 DOM API (
window.scrollTo) 的结合。 - CSS Transition 的视觉效果优化。
代码解析
模板结构 (Template)
<template>
<!-- 使用 Transition 包裹,给按钮加上场/退场动画 -->
<Transition name="slide-up">
<button
v-if="visible"
@click="scrollToTop"
class="back-to-top-btn"
:title="tooltip"
>
<!-- 内嵌 SVG 向上箭头图标,避免引入外部图片资源 -->
<svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 10l7-7m0 0l7 7m-7-7v18"
/>
</svg>
</button>
</Transition>
</template>
<Transition>:Vue 提供的内置组件,当v-if切换时自动应用 CSS 过渡类名。- SVG 图标:直接内嵌 SVG,减少网络请求,且通过
currentColor继承父元素颜色,方便样式控制。
逻辑核心 (Script)
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
// 1. 定义 Props,使用 withDefaults 设置默认值
const props = withDefaults(defineProps<{
threshold?: number // 滚动阈值,超过此值显示按钮
tooltip?: string // 鼠标悬停提示文字
}>(), {
threshold: 300,
tooltip: '返回顶部'
})
// 2. 响应式状态:控制按钮显示/隐藏
const visible = ref(false)
// 3. 滚动处理函数:判断当前滚动位置
const handleScroll = () => {
visible.value = window.scrollY > props.threshold
}
// 4. 核心动作:执行滚动到顶部
const scrollToTop = () => {
window.scrollTo({
top: 0,
behavior: 'smooth' // 关键:实现平滑滚动,而非瞬间跳转
})
}
// 5. 生命周期:挂载时监听滚动事件
onMounted(() => {
window.addEventListener('scroll', handleScroll)
})
// 6. 生命周期:卸载时移除监听(非常重要,防止内存泄漏)
onUnmounted(() => {
window.removeEventListener('scroll', handleScroll)
})
</script>
withDefaults:配合 TypeScript 的defineProps,优雅地给 Props 赋默认值。onUnmounted:这是新手容易忽略的点。如果组件销毁了但不移除window上的事件监听器,会导致内存泄漏或报错。behavior: 'smooth':原生 API,比自己写定时器动画要高效得多。
样式设计 (Style)
<style scoped>
.back-to-top-btn {
position: fixed; /* 固定定位,悬浮在页面右下角 */
bottom: 2rem;
right: 2rem;
width: 3rem;
height: 3rem;
background: #4f46e5; /* 靛蓝色,现代感强 */
color: white;
border: none;
border-radius: 50%; /* 圆形按钮 */
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 12px rgba(79, 70, 229, 0.4); /* 柔和阴影 */
transition: all 0.3s ease;
z-index: 1000; /* 确保层级够高,不被其他元素遮挡 */
}
.back-to-top-btn:hover {
background: #4338ca; /* 悬停变深色 */
transform: translateY(-4px); /* 悬停上浮效果 */
box-shadow: 0 8px 20px rgba(79, 70, 229, 0.5);
}
.back-to-top-btn:active {
transform: translateY(-2px); /* 点击时的按压反馈 */
}
/* Transition 动画对应的 CSS 类 */
.slide-up-enter-active,
.slide-up-leave-active {
transition: all 0.3s ease;
}
.slide-up-enter-from,
.slide-up-leave-to {
opacity: 0;
transform: translateY(20px); /* 从下方 20px 处滑入 */
}
</style>
- 交互反馈:通过
:hover和:active伪类配合transform,让按钮有 “呼吸感”。 - 动画类名:
slide-up-enter-from定义了初始状态(透明且位置偏下),Vue 会自动在合适的时机切换类名。
如何在项目中使用
- 将上述代码保存为
BackToTop.vue。 - 在你的页面组件(通常是
Layout或App.vue)中引入:
<template>
<div class="very-long-page-content">
<!-- 你的页面内容 -->
</div>
<!-- 直接使用,也可以传入 threshold 自定义显示距离 -->
<BackToTop :threshold="500" />
</template>
<script setup lang="ts">
import BackToTop from './components/BackToTop.vue'
</script>