目标:实现当组件进入可视区域在加载数据或者发送请求。
背景:父组件为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重新置为空数组