很多小伙伴写过在原生js里面回到顶部的功能,真正在上手react项目后,如何实现这一功能。最近在做项目的过程中就有这个需求,由于项目的技术栈是基于Antd-mobile+ahooks,里面并没有回到顶部功能的组件(我们知道antd是有的,所以am啥时候能跟进一点),话不多说,下面就简单实现一个回到顶部功能。
组件封装
因为回到顶部在许多页面都要使用,所以我们最好把它抽离成一个小组件得以复用,我们简单封装一下,代码如下:
import styles from './style.module.scss'
import backToTop from '../imgs/backToTop.svg'
interface Props {
visible: boolean
back: () => void
}
export default function BackToTop(props: Props) {
return (
<div
className={styles.backToTop}
onClick={() => {
props.back()
}}
style={
props.visible ? { visibility: 'visible' } : { visibility: 'hidden' }
}>
<div className={styles.back}>
<img src={backToTop} alt="" />
<span>返回顶部</span>
</div>
</div>
)
}
样式简单写:
.backToTop {
transition: all ease-in-out .05s;
height: 20px;
position: fixed;
bottom: 100px;
scroll-behavior: smooth;
right: 20px;
}
.back {
font-size: 12px;
color: #007AFA;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
}
组件接收两个参数,visible
控制组件显示隐藏,back
是传入的滚动到顶部的回调函数。
接下来就在页面中实际使用一下这个组件,很多人可能首先想到的是监听scroll事件,判断scrollTop的距离来进行控制组件的显示隐藏,这次我们借助于ahooks这个开源库来实现,体验效果会更佳。ahooks 是由蚂蚁 umi 团队开源的React Hooks 工具库。ahooks 是基于 React Hooks进行封装的,提供了非常好用的一系列hooks,极大地降低了开发难度,提升了开发效率。
代码实现
我们首先导入react相关hooks和刚封装的组件
import React, { useEffect, useRef, useState } from 'react'
import BackToTop from '../components/BackToTop'
import styles from '../styles/Demo.module.scss'
import { useScroll } from 'ahooks'
export default function Demo() {
// 控制回到顶部组件显示的状态
const [backVisible, setBackVisible] = useState(false)
// ref绑定在我们要监听的滚动区域Dom元素上
const backRef = useRef<HTMLDivElement>(null)
//
const scroll = useScroll(backRef)
useEffect(() => {
if (scroll.top > 400) {
setBackVisible(true)
} else {
setBackVisible(false)
}
}, [scroll.top])
return (
<div className={styles.list} ref={backRef}>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
<BackToTop
visible={backVisible}
back={() => {
backRef.current?.scrollTo({ behavior: 'smooth', top: 0, left: 0 })
}}
/>
</div>
)
节流
这样一个基本的回到顶部功能就实现了,但是上述代码有一个不合理的地方,只要稍微滚动一下页面,就会触发页面的滚动事件的频繁触发,这就引发性能问题,因此,我们来实现一个节流函数:
export default function throttle(func: Function, delay: number) {
let timer: NodeJS.Timeout | null
return function () {
if (!timer) {
timer = setTimeout(function (this: typeof func) {
timer = null
func.apply(this, arguments)
}, delay)
}
}
}
最后代码长这样:
useEffect(() => {
const func = throttle(() => {
if (scroll.top > 1000) {
setBackVisible(true)
} else {
setBackVisible(false)
}
}, 500)
func()
}, [scroll.top])