这里给大家分享我在网上总结出来的一些知识 ,希望对大家有所帮助
这样 ,我们就完成了一个基本的列表需求的逻辑部分 。大部分的列表需求都是类似的逻辑 ,既然如此 ,Dont Repeat Yourself!(不要重复写你的代码!) ,我们来把它封装成通用的方法:
首先 ,既然是通用的 ,会在多个地方使用 ,那么数据肯定不能乱了 ,我们要在每次使用 useList的时候都拿到独属于自己的那一份数据 。是不是感觉很熟悉?对的 ,就是以前的 data为什么是一个函数那个问题!所以我们的 useList是需要导出一个函数 ,我们从这个函数中获取数据与方法 。让这个函数导出一个对象/数组 ,这样调用的时候 解构就可以拿到我们需要的变量和方法了
// useList.js 中
const useList = () => {
// 待补充的函数体
return {}
}
export default useList
然后,不同的地方调用的接口肯定不一样 ,我们想一次封装 ,不再维护,那么咱们干脆在使用的时候 ,把调用接口的方法传进来就可以了
// useList.js 中
import { ref } from vue
const useList = (listReq) => {
if (!listReq) {
return new Error(请传入接口调用方法!)
}
const list = ref([])
const getList = () => {
listReq().then((res) => (list.value = res.list))
}
return {
list,
getList,
}
}
export default useList
这样 ,我们就完成了一个简单的列表hooks ,使用的时候直接:
// setup中
import useList from @/utils
const { list, getList } = useList(axios.get(url/to/get/list))
getList()
等等!列表好像不涉及到DOM操作 ,那咱们再偷点懒 ,直接在useList内部就调用了吧!
// useList.js中
import { ref } from vue
const useList = (listReq) => {
if (!listReq) {
return new Error(请传入接口调用方法!)
}
const list = ref([])
const getList = () => {
listReq().then((res) => (list.value = res.list))
}
getList() // 直接初始化 ,省去在外面初始化的步骤
return {
list,
getList,
}
}
export default useList
这时有老哥要说了 ,那我要是一个页面有多个列表怎么办?嘿嘿 ,别忘了 ,解构的时候是可以重命名的
// setup中
const { list: goodsList, getList: getGoodsList } = useList(
axios.get(/url/get/goods)
)
const { list: recommendList, getList: getRecommendList } = useList(
axios.get(/url/get/goods)
)
这样 ,我们就同时在一个页面里面 ,获取到了商品列表以及推荐列表所需要的变量与方法啦
带分页版
如果数据量比较大的话 ,所有的数据全部拿出来渲染显然不合理,所以我们一般要进行分页处理 ,我们来分析一下这个需求:
需求分析
要分页 ,那咱们肯定要告诉后端当前请求的是第几页 、每页多少条,可能有些地方还需要展示总共有多少条 ,为了方便管理 ,咱们把这些分页数据统一放到 pageInfo对象中
分页了 ,那咱们肯定还有加载下一页的需求 ,需要一个 loadmore函数
分页了 ,那咱们肯定还会有刷新的需求 ,需要一个 initList函数
代码实现
需求分析好了 ,代码实现起来就简单了 ,废话少说 ,上代码!
// useList.js中
import { ref } from vue
const useList = (listReq) => {
if (!listReq) {
return new Error(请传入接口调用方法!)
}
const list = ref([])
// 新增pageInfo对象保存分页数据
const pageInfo = ref({
pageNum: 1,
pageSize: 10,
total: 0,
})
const getList = () => {
// 分页数据作为参数传递给接口调用函数即可
// 将请求这个Promise返回出去 ,以便链式then
return listReq(pageInfo.value).then((res) => {
list.value = res.list
// 更新总数量
pageInfo.value.total = res.total
// 返回出去 ,交给then默认的Promise ,以便后续使用
return res
})
}
// 新增加载下一页的函数
const loadmore = () => {
// 下一页,那咱们把当前页自增一下就行了
pageInfo.value.pageNum += 1
// 如果已经是最后一页了(本次获取到空数组)
getList().then((res) => {
if (!res.list.length) {
uni.showToast({
title: 没有更多了,
icon: none,
})
}
})
}
// 新增初始化
const initList = () => {
// 初始化一般是要把所有的查询条件都初始化 ,这里只有分页 ,咱就回到第一页就行
pageInfo.value.pageNum = 1
getList()
}
getList()
return {
list,
getList,
loadmore,
initList,
}
}
export default useList
完工!跑起来试试,Perfec......等等 ,好像不太对...
加载更多 ,应该是把两次请求的数据合并到一起渲染出来才对 ,这怎么直接替换掉了?
回头看看代码 ,原来是咱们漏了拼接的逻辑 ,补上 ,补上
// useList.js中
// ...省略其余代码
const getList = () => {
// 分页数据作为参数传递给接口调用函数即可
return listReq(pageInfo.value).then((res) => {
// 当前页不为1则是加载更多 ,需要拼接数据
if (pageInfo.value.pageNum === 1) {
list.value = res.list
} else {
list.value = [...list.value, ...res.list]
}
pageInfo.value.total = res.total
return res
})
}
// ...省略其余代码
带 hooks 版
上面的分页版 ,我们给出了 加载更多和 初始化列表功能 ,但是还是要手动调用 。仔细想想 ,咱们刷新列表 ,一般都是在页面顶部下拉的时候刷新的;而加载更多 ,一般都是在滚动到底部的时候加载的 。既然都是一样的触发时机,那咱们继续封装吧!
需求分析
uni-app 中提供了 onPullDownRefresh和 onReachBottom钩子 ,在其中处理相关逻辑即可
有些列表可能不是在页面中 ,而是在 scroll-view中,还是需要手动处理 ,因此上面的函数咱们依然需要导出
代码实现
钩子函数(hooks)接受一个回调函数作为参数 ,咱们直接把上面的函数传入即可
需要注意的是 ,uni-app 中 ,下拉刷新的动画需要手动关闭 ,咱们还需要改造一下 listReq函数
// useList中
import { onPullDownRefresh, onReachBottom } from @dcloudio/uni-app
// ...省略其余代码
onPullDownRefresh(initList)
onReachBottom(loadmore)
const getList = () => {
// 分页数据作为参数传递给接口调用函数即可
return listReq(pageInfo.value)
.then((res) => {
// ...省略其余代码
})
.finally((info) => {
// 不管成功还是失败 ,关闭下拉刷新的动画
uni.stopPullDownRefresh()
// 在最后再把前面返回的消息return出去 ,以便后续处理
return info
})
}
// ...省略其余代码
带参数
其实在实际开发中 ,我们在发起请求时可能还需要其他的参数 ,上面我们都是固定的只有分页的参数 ,可以稍加改造
需求分析
可能大家第一反应是多一个参数 ,或者用 展开运算符 (...)再定义一个形参就行了 。这么做肯定是没问题的 ,不过在这里的话不够优雅~
我们这里是要增加一个传给后端的参数,一般都是一起以 JSON 对象的形式传过去 ,既然如此 ,那咱们把所有的参数都用一个对象接受,发起请求的时候和分页参数对象合并为一个对象 ,代码的可读性会更高 ,使用者在使用时也可以自由地定义 key-value 键值对
代码实现
// useList中
const useList = (listReq, data) => {
// ...省略其余代码
// 判断第二个参数是否是对象 ,以免后面使用展开运算符时报错
if (data && Object.prototype.toString.call(data) !== [object Object]) {
return new Error(额外参数请使用对象传入)
}
const getList = () => {
const params = {
...pageInfo.value,
...data,
}
return listReq(params).then((res) => {
// ...省略其余代码
})
}
// ...省略其余代码
}
// ...省略其余代码
带默认配置版
有些时候我们的列表是在页面中间 ,不需要触底加载更多;有时候我们可能需要在不同的地方调用相同的接口 ,但是需要获取的数据量不一样....
为了适应各种各样的需求 ,我们可以稍加改造 ,添加一个带有默认值的配置对象 ,
// useList.js中
const defaultConfig = {
pageSize: 10, // 每页数量 ,其实也可以在data里面覆盖
needLoadMore: true, // 是否需要下拉加载
data: {}, // 这个就是给接口用的额外参数了
// 还可以根据自己项目需求添加其他配置
}
// 添加一个有默认值的参数 ,依然满足大部分列表页传入接口即可使用的需求
const useList = (listReq, config = defaultConfig) => {
// 解构的时候赋上初始值 ,这样即使配置参数只传了一个参数 ,也不影响其他的配置
const {
pageSize = defaultConfig.pageSize,
needLoadMore = defaultConfig.needLoadMore,
data = defaultConfig.data,
} = config
// 应用相应的配置
if (needLoadMore) {
onReachBottom(loadmore)
}
const pageInfo = ref({
pageNum: 1,
pageSize,
total: 0,
})
// ...省略其余代码
}
// ...省略其余代码
这样一来,咱们就实现了一个满足大部分移动端列表页的逻辑复用 hooks
web 端的几乎只有加载更多(翻页)的时候逻辑不太一样,不需要拼接数据,在封装的时候可以把分页器的处理逻辑一起封装进来
本文转载于:
https://juejin.cn/post/7165467345648320520
如果对您有所帮助 ,欢迎您点个关注 ,我会定时更新技术文档,大家一起讨论学习 ,一起进步 。
声明:本站所有文章 ,如无特殊说明或标注 ,均为本站原创发布 。任何个人或组织 ,在未征得本站同意时 ,禁止复制 、盗用 、采集 、发布本站内容到任何网站 、书籍等各类媒体平台 。如若本站内容侵犯了原著者的合法权益 ,可联系我们进行处理 。