【源码学习】第28期 | 如何实现一键返回顶部?

467 阅读2分钟

前言

    当开发页面内容较多,如介绍官网产品之类【只有一个产品的当我没说~】的,应该有不少童鞋都碰到过一键回到顶部的需求,那么一键回到顶部具体是怎么实现的呢?今天就一起来看一下element-plus的el-backtop的源码,研究一下具体的实现!

收获清单

  • 源码调试分析
  • 回到顶部组件实现

组件介绍

  • 功能
    返回页面顶部的操作按钮。
  • 属性
名称说明类型默认值
target触发滚动的对象string
visibility-height滚动高度达到此参数值才出现number200
right控制其显示位置,距离页面右边距number40
bottom控制其显示位置,距离页面底部距离number40

源码下载

 git clone https://github.com/element-plus/element-plus.git
 cd element-plus
 pnpm install
 // 本地打开文档
 pnpm docs:dev
 // 本地运行例子
 pnpm run dev
  • 利用vue-tools打开源码位置     我们可以在play\src\App.vue引入官网例子,然后运行pnpm run dev,这样就可以借助vue-tools打开源码位置了,效果如下:

el-top.gif

源码分析

  • 入口文件 index.ts
import { withInstall } from '@element-plus/utils'
import Backtop from './src/backtop.vue'
export const ElBacktop = withInstall(Backtop)
export default ElBacktop
export * from './src/backtop'
export type { BacktopInstance } from './src/instance'

    入口文件的作用主要是全局注册ElBacktop组件,并且导出相关实例和组件供全局使用,我们也可以在这里打debugger调试一下,看withInstall的作用: el-top调试.gif

  • backtop.vue
<template>
  <transition :name="`${ns.namespace.value}-fade-in`">
    <div
      v-if="visible"
      :style="backTopStyle"
      :class="ns.b()"
      @click.stop="handleClick"
    >
     <!-- 可以自定义backtop组件的显示 -->
      <slot>
        <el-icon :class="ns.e('icon')"><caret-top /></el-icon>
      </slot>
    </div>
  </transition>
</template>
<script lang="ts" setup>
import { computed } from 'vue'
import { ElIcon } from '@element-plus/components/icon'
import { CaretTop } from '@element-plus/icons-vue'
import { useNamespace } from '@element-plus/hooks'
import { backtopEmits, backtopProps } from './backtop'
import { useBackTop } from './use-backtop'
const COMPONENT_NAME = 'ElBacktop'
defineOptions({
  name: COMPONENT_NAME,
})
// 支持传入visibilityHeight、target、 right、bottom等属性
const props = defineProps(backtopProps)
// emit click事件
const emit = defineEmits(backtopEmits)
// 命名空间
const ns = useNamespace('backtop')
// 响应点击事件和获取控制backtop组件显示隐藏的属性
const { handleClick, visible } = useBackTop(props, emit, COMPONENT_NAME)
// 样式
const backTopStyle = computed(() => ({
  right: `${props.right}px`,
  bottom: `${props.bottom}px`,
}))
</script>
  • use-backtop.ts
import { onMounted, ref, shallowRef } from 'vue'
import { useEventListener, useThrottleFn } from '@vueuse/core'
import { throwError } from '@element-plus/utils'
import type { SetupContext } from 'vue'
import type { BacktopEmits, BacktopProps } from './backtop'

export const useBackTop = (
  props: BacktopProps,
  emit: SetupContext<BacktopEmits>['emit'],
  componentName: string
) => {
  const el = shallowRef<HTMLElement>()
  const container = shallowRef<Document | HTMLElement>()
  const visible = ref(false)
  // 滚动到大于可视高度的位置时显示el-backtop组件
  const handleScroll = () => {
    if (el.value) visible.value = el.value.scrollTop >= props.visibilityHeight
  }
  // 利用scrollTo使界面滚动到指定位置,其中smooth 表示平滑滚动并产生过渡效果
  const handleClick = (event: MouseEvent) => {
    el.value?.scrollTo({ top: 0, behavior: 'smooth' })
    emit('click', event)
  }
  // 创建一个节流函数,在300 毫秒内最多执行 handleScroll 一次的函数
  const handleScrollThrottled = useThrottleFn(handleScroll, 300, true)
  // 给document设置滚动事件的监听器
  useEventListener(container, 'scroll', handleScrollThrottled)

  // onMounted时赋值,若指定target则目标元素为target否则将文档对象(document)的根元素的只读属性(如 HTML 文档的 <html> 元素)赋值给el
  onMounted(() => {
    container.value = document
    el.value = document.documentElement

    if (props.target) {
      el.value = document.querySelector<HTMLElement>(props.target) ?? undefined
      if (!el.value) {
        throwError(componentName, `target does not exist: ${props.target}`)
      }
      container.value = el.value
    }
    // Give visible an initial value, fix #13066
    handleScroll()
  })

  return {
    visible,
    handleClick,
  }
}

    由以上代码我们可以总结出el-backtop主要是通过scrollTo来实现一键回到顶部的效果,同时给监听的滚动事件设置节流的优化手段也是我们在日常开发中可以借鉴的~

总结

    今天的源码学习就告一段落,源码调试的方式有多种,也可以借助测试用例来调试。el-backtop组件的源码比较简单,当然可能主要还是因为功能比较简单,当想要学源码却无从下手的时候就可以挑一些比较易懂的开始(如这一期),因为这样可以逐渐增加自己学习的动力~