目标:实现当组件进入可视区域在加载数据或者发送请求。
背景:父组件为vxe-table构成的组件、子组件为table的某一列,这一列的数据通过接口返回,有多少条表格数据就会请求多少次接口,为了提升性能,所以采用接口懒加载,但是需要在回滚的时候不重复请求或者加载数据
- 使用 @vueuse/core 中的 useIntersectionObserver 来实现监听进入可视区域行为,但是必须配合vue3.0的组合API的方式才能实现。
- 对某个板块进行数据懒加载,首先要获取到这个dom元素,然后用useIntersectionObserver来监听这个dom,一旦可视区进入了这个dom元素这里,就可以进行请求数据接口
- 安装@vueuse/core包,它封装了一些常见的交互逻辑
- 通过状态机保存在列表上已经加载过了的数据,并打上已经加载过了的标签:isLaded:rue,回滚时就对比传入子组件的row数据的id与存入状态机的数据是否有id相同的一条,并查看是否 已存在isLoaded
npm i @vueuse/core@4.9.0
步骤:
- 理解 useIntersectionObserver 的使用,各个参数的含义
- 封装 useLazyData 函数,作为数据懒加载公用函数
- 把 index.vue页面里数据改造成懒加载方式
页面准备:
src/hook.ts存放懒加载逻辑的函数
import { useIntersectionObserver } from '@vueuse/core'
import { ref } from 'vue'
/**
* 用于懒加载数据
* @param {*} apiFn 懒加载数据的接口
* @returns target: 需要绑定的DOM对象 result: 结果数据
*/
export const useLazyData = (apiFn: Function) => {
const target = ref(null)
const result = ref()
let stopObserver // 保存观察者的停止函数
const { stop } = useIntersectionObserver(target, ([{ isIntersecting }], observerElement) => {
if (isIntersecting) {
stopObserver() // 调用之前保存的停止函数
apiFn().then((data) => {
if (data) {
result.value = data
}
})
}
})
stopObserver = stop // 保存观察者的停止函数
return { target, result,stopObserver }
}
在状态机中定义好状态机的一切
[RootMutations.LOADED_TEST_DATA](state: RootState, value) {
state.loadedTestData = value
},
子组件:绑定target,使用懒加载
<section ref="target">
<span>{{ newData?.end_time }}</span>
</section>
import { defaultProps } from './props'
const props = defineProps(defaultProps)
const emits = defineEmits(['update'])
const { modelValue, endTime } = toRefs(props)
const newData: Ref<App.BatteryTest> = ref(unref(modelValue))
//封装接口请求
const getDeadline = () => {
.test(参数, {
//逻辑代码
})
.then(([res]) => {
if (res?.status === 200) {
nextTick(() => {
newData.value = { ...unref(newData), end_time: dateFormat(res?.data?.Deadline / 1000) + '' }
emits('update', unref(newData))
updateLoadedData() //更新已经加载的数据
})
return dateFormat(res?.data?.Deadline / 1000) + ''//这里返回给hook中定义的useLazyData中的result
}else {
nextTick(() => {
newData.value = { ...unref(newData), end_time: '' }
emits('update', unref(newData))
updateLoadedData() //更新已经加载的数据
})
return ''
}
})
function dateFormat(date: any) {
//转变导出报表记录日期格式
if (date == undefined || date == 0) {
return ''
}
return moment(date * 1000).format('YYYY-MM-DD HH:mm:ss')
}
//更新已经加载的数据isLoaded: true,存入状态机
function updateLoadedData() {
let bbarr = cloneDeep(store.state.loadedTestData)
bbarr.push({ ...unref(newData), isLoaded: true })
store.commit('LOADED_TEST_DATA',bbarr)
}
const { target, result, stopObserver } = useLazyData(getDeadline)
//监听穿入子组件的props数据
watch(
modelValue,
newValue => {
let originArr = cloneDeep(store.state.loadedTestData)
if (originArr.length > 0) {
// 停止观察已经加载的节点
const hasSameId = originArr.some(item => item.id === unref(modelValue)?.id && item.isLoaded)
if (hasSameId) {
stopObserver()
const item = originArr.find(item => item.id === unref(modelValue)?.id && item.isLoaded)
newData.value.end_time = formateEndTime(item.end_time)//重新赋值为原来的数据
}
}
},
{ deep: true, immediate: true }
)
父组件:
<vxe-column
:key="index"
v-bind.sync="column"
:field="column.prop"
:title="column.label"
width="item.minWidth"
show-overflow
:formatter="column.prop === 'test_state' ? formatTestState : null"
:sortable="column.prop !== 'end_time'"
v-if="column.checked"
>
<template v-else-if="column.prop === 'end_time'" #default="{ row }">
<!-- <battary-loading :end-time="row.end_time" :model-value.sync="row" :class="[endTimeClass, endTimeBreathLight(row.end_time)]"></battary-loading> -->
<battary-loading :end-time="row.end_time" :model-value.sync="row" @update="getTableRow"></battary-loading>
</template>
<vxe-column/>
//在nMounted里面重置为[]
onMounted(() => {
store.commit('LOADED_TEST_DATA',[])
})
//如果有分页,可在分页事件中将OADED_TEST_DATA重新置为空数组