小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
Html
很简单,我们的核心主体内容全部交给slot,其他的逻辑行为全部交给此组件
<template>
<div ref="wrapper">
<slot />
</div>
</template>
js
由于此组件基于better-scroll(以下全部简称为BS),所以先引入BS的核心包,并按需引入其对应的插件。我们给BS添加鼠标滚动的行为
import BScroll, { BScrollInstance, Options } from '@better-scroll/core'
import MouseWheel from '@better-scroll/mouse-wheel'
BScroll.use(MouseWheel)
function _init(elm: HTMLElement | string, option: Options): BScrollInstance {
const scroll = new BScroll(elm, { ...option })
return scroll
}
我们创建了一个_init函数进行BS的实例化。
props
props: {
/**
* 1 滚动的时候会派发scroll事件,会截流。
* 2 滚动的时候实时派发scroll事件,不会截流。
* 3 除了实时派发scroll事件,在swipe的情况下仍然能实时派发scroll事件
*/
probeType: {
type: Number,
default: 3
},
/**
* 点击列表是否派发click事件
*/
click: {
type: Boolean,
default: true
},
/**
* 是否开启纵向滚动
*/
scrollY: {
type: Boolean,
default: true
},
/**
* 是否开启横向滚动
*/
scrollX: {
type: Boolean,
default: false
},
/**
* 是否派发滚动事件
*/
listenScroll: {
type: Boolean,
default: false
},
/**
* 列表的数据
*/
data: {
type: Array,
default: null
},
/**
* 当数据更新后,刷新scroll的延时。
*/
refreshDelay: {
type: Number,
default: 20
}
},
setup
setup(props, ctx) {
const wrapper = ref<HTMLElement | null>(null) // dom
const scroll = ref<null | BScrollInstance>(null) // BScroll
nextTick(() => {
scroll.value = _init(wrapper.value as HTMLElement, {
probeType: props.probeType,
click: props.click,
scrollX: props.scrollX,
scrollY: props.scrollY,
mouseWheel: true
})
if (props.listenScroll) {
scroll.value.on('scroll', (pos: any) => {
ctx.emit('scroll', pos)
})
}
})
return { wrapper }
}
我们在nextTick中,进行BS实例化,确保dom被挂载。
代理BS方法
为确保组件整洁,父组件使用时不去调用底层方法,我们对BS方法进行代理
const disable = () => {
// 代理better-scroll的disable方法
scroll.value && scroll.value.disable()
}
const enable = () => {
// 代理better-scroll的enable方法
scroll.value && scroll.value.enable()
}
const refresh = () => {
// 代理better-scroll的refresh方法
scroll.value && scroll.value.refresh()
}
function scrollTo() {
// 代理better-scroll的scrollTo方法
scroll.value &&
scroll.value.scrollTo.apply(scroll.value, arguments as any)
}
function scrollToElement() {
// 代理better-scroll的scrollToElement方法
scroll.value &&
scroll.value.scrollToElement.apply(scroll.value, arguments as any)
}
watch(props.data, () => {
setTimeout(() => {
refresh()
}, props.refreshDelay)
})
return { wrapper, disable, enable, refresh, scrollTo, scrollToElement }
source code
<template>
<div ref="wrapper">
<slot />
</div>
</template>
<script lang='ts'>
import { defineComponent, ref, nextTick, watch } from 'vue'
import BScroll, { BScrollInstance, Options } from '@better-scroll/core'
import MouseWheel from '@better-scroll/mouse-wheel'
BScroll.use(MouseWheel)
function _init(elm: HTMLElement | string, option: Options): BScrollInstance {
const scroll = new BScroll(elm, { ...option })
return scroll
}
export default defineComponent({
name: 'Scroller',
props: {
/**
* 1 滚动的时候会派发scroll事件,会截流。
* 2 滚动的时候实时派发scroll事件,不会截流。
* 3 除了实时派发scroll事件,在swipe的情况下仍然能实时派发scroll事件
*/
probeType: {
type: Number,
default: 3
},
/**
* 点击列表是否派发click事件
*/
click: {
type: Boolean,
default: true
},
/**
* 是否开启纵向滚动
*/
scrollY: {
type: Boolean,
default: true
},
/**
* 是否开启横向滚动
*/
scrollX: {
type: Boolean,
default: false
},
/**
* 是否派发滚动事件
*/
listenScroll: {
type: Boolean,
default: false
},
/**
* 列表的数据
*/
data: {
type: Array,
default: null
},
/**
* 当数据更新后,刷新scroll的延时。
*/
refreshDelay: {
type: Number,
default: 20
}
},
setup(props, ctx) {
const wrapper = ref<HTMLElement | null>(null) // dom
const scroll = ref<null | BScrollInstance>(null) // BScroll
nextTick(() => {
scroll.value = _init(wrapper.value as HTMLElement, {
probeType: props.probeType,
click: props.click,
scrollX: props.scrollX,
scrollY: props.scrollY,
mouseWheel: true
})
if (props.listenScroll) {
scroll.value.on('scroll', (pos: any) => {
ctx.emit('scroll', pos)
})
}
})
const disable = () => {
// 代理better-scroll的disable方法
scroll.value && scroll.value.disable()
}
const enable = () => {
// 代理better-scroll的enable方法
scroll.value && scroll.value.enable()
}
const refresh = () => {
// 代理better-scroll的refresh方法
scroll.value && scroll.value.refresh()
}
function scrollTo() {
// 代理better-scroll的scrollTo方法
scroll.value &&
scroll.value.scrollTo.apply(scroll.value, arguments as any)
}
function scrollToElement() {
// 代理better-scroll的scrollToElement方法
scroll.value &&
scroll.value.scrollToElement.apply(scroll.value, arguments as any)
}
watch(props.data, () => {
setTimeout(() => {
refresh()
}, props.refreshDelay)
})
return { wrapper, disable, enable, refresh, scrollTo, scrollToElement }
}
})
</script>
总结
借鉴黄轶老师的better-scroll的vue2封装原理进行的vue3版本的二次封装。
这个组件的源码对于<script setup>已经有点老了。希望大家可以对源码进行魔改,因为vue3的好多api,例如defineProps, withDefault, defineExpose ···都可以应用上,确保组件的安全和规范