最近按照产品的要求对列表进行了重构,把以前的普通的翻页列表改成了无限滚动加载列表——滚动至底部时,加载更多数据——和移动应用的翻页效果类似。借助 vue-infinite-scroll 组件很快就实现了无限滚动加载功能。
发现问题
酷是很酷。但是问题也来了:需要改动分页的页面还不少:图片列表、声音列表、视频列表都需要改动。使用 “CV”大法,工作量不算很大,然而“CV”大法的代码很难维护。有没有办法复用这个逻辑呢?我们先前的分页采用混入(mixin)来复用的。实践证明这种方法不是太靠谱。
- 命名空间冲突:模块多了,不严格遵循命名规范,混入很容易导致命名冲突。
- 数据来源不清晰的:不小心会误删代码。
记得有个刚来的小伙伴对项目代码不是很清楚,他看到一个页面有很多属性和方法都没有引用或调用,就自作主张地将“多余”代码删了,结果报错了,他不知道我们的代码使用了混入。
分析问题
有没有其他办法可以实现无限滚动加载复用?因为前不久系统学习了 Vue 插槽,尤其对作用域插槽影响深刻。我就琢磨着使用 <slot>
来实现可复用的无限滚动加载列表。
基本原理
- 我们可以像对组件传递 props 那样,向一个插槽的出口上插槽 props 对象:
- 当需要接收插槽 props 时,默认插槽通过子组件标签上的 v-slot 指令,直接接收到了一个插槽 props 对象:
- 子组件传入插槽的 props 作为了 v-slot 指令的值,可以在插槽内的表达式中访问。
注意:具名作用域插槽的工作方式也是类似的,插槽 props 可以作为 v-slot 指令的值被访问到:v-slot:name="slotProps"。可以在 v-slot 中使用解构。
我们可以定义一个 InfiniteScrollList 组件,它只包括了逻辑(比如无限滚动加载)而不需要自己渲染内容,视图输出通过作用域插槽全权交给了消费者组件( ImageList、AudioList 等组件),这样就是很好的实现了代码的重用。我们将这种类型的组件和 React 中的容器组件类似。
InfiniteScrollList 组件实现
新建 InfiniteScrollList.vue
,添加下述代码:
- template
<div
class="infinite-scroll"
v-infinite-scroll="loadMore"
:infinite-scroll-disabled="!busy"
>
<slot :list="list" />
<transition name="fade">
<div v-show="!busy" class="loader">{{ message }}</div>
</transition>
</div>
- script
import axios from "axios";
export default {
name: "InfiniteScrollList",
data() {
return {
busy: false,
finished: false,
list: [],
total: 0,
param: {
offset: 0,
limit: 30,
type: 1,
},
};
},
computed: {
message() {
return !this.finished ? "Load more..." : "No more~";
},
},
methods: {
loadMore() {
// ...
},
};
- style
.loader {
position: fixed;
width: 100%;
bottom: 0;
background-color: #eeeeee;
text-align: center;
font-size: 12px;
line-height: 36px;
}
.fade-enter-active {
animation: fade-in 0.5s;
}
.fade-leave-active {
animation: fade-in 2s 0.5s reverse;
}
@keyframes fade-in {
0% {
opacity: 0;
}
50% {
opacity: 50;
}
100% {
opacity: 100;
}
}
实现 ImageList 组件
新建 InfiniteScrollList.vue
,添加下述代码:
<template>
<InfiniteScrollList v-slot="{ list }">
<ul>
<li v-for="(item, index) in list" :key="index">
{{ item.name }} -- {{ new Date(item.create * 1000) | formatDate }}
</li>
</ul>
</InfiniteScrollList>
</template>
//...
其他组件类似,可访问 codesandbox 查看项目代码。
总结
本文简述了怎样使用作用域插槽自定义一个可高级列表组件,它只封装了可重用的逻辑而不需要自己渲染内容,视图输出通过作用域插槽全权交给了消费者组件。
评论 (0)