记录--这样封装列表 hooks,一天可以开发 20 个页面
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
这样封装列表 hooks,一天可以开发 20 个页面
前言
在做移动端的需求时,我们经常会开发一些列表页,这些列表页大多数有着相似的功能:分页获取列表、上拉加载、下拉刷新···
在 Vue
出来 compositionAPI
之前,我们想要复用这样的逻辑还是比较麻烦的,好在现在 Vue2.7+
都支持 compositionAPI
语法了,这篇文章我将 手把手
带你用 compositionAPI
封装一个名为 useList
的 hooks
来实现列表页的逻辑复用。
基础版
需求分析
一个列表,最基本的需求应该包括: 发起请求,获取到列表的数组,然后将该数组渲染成相应的 DOM
节点。要实现这个功能,我们需要以下变量:
- list : 数组变量,用来存放后端返回的数据,并在
template
模板中使用v-for
来遍历渲染成我们想要的样子。 - listReq: 发起 http 请求的函数,一般是
axios
的实例
代码实现
有了上面的分析,我们可以很轻松地在 setup
中写出如下代码:
import { ref } from 'vue'
import axios from 'axios' // 简单示例,就不给出封装axios的代码了 const list = ref([]) const listReq = () => {
axios.get('/url/to/getList').then((res) => {
list.value = res.list
})
} listReq()
这样,我们就完成了一个基本的列表需求的逻辑部分。大部分的列表需求都是类似的逻辑,既然如此,Don't 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
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。
记录--这样封装列表 hooks,一天可以开发 20 个页面的更多相关文章
- CozyRSS开发记录5-订阅列表栏里的项
CozyRSS开发记录5-订阅列表栏里的项 1.订阅列表栏里的项的原型图 这里列表项依然参考傲游的RSS阅读器,以后可能会微调. 2.使用ControlTemplate来定制ListBoxItem 给 ...
- 我的一个PLSQL函数 先查询再插入数据库的函数 动态SQL拼接查询条件、通用游标、记录定义(封装部分查询字段并赋值给游标)、insert select 序列、常量【我】
先查询再插入数据库的函数 CREATE OR REPLACE FUNCTION F_REVENUE_SI(l_p_cd in Varchar2, l_c_cd in Varchar2, l_prod_ ...
- 循序渐进VUE+Element 前端应用开发(20)--- 使用组件封装简化界面代码
VUE+Element 前端应用,比较不错的一点就是界面组件化,我们可以根据重用的指导方针,把界面内容拆分为各个不同的组合,每一个模块可以是一个组件,也可以是多个组件的综合体,而且这一个过程非常方便. ...
- Android封装TitleBar基本适用所有常规开发
Android封装TitleBar基本适用所有常规开发 github地址:https://github.com/SiberiaDante/SiberiaDanteLib/blob/master/sib ...
- 03 基于umi搭建React快速开发框架(封装列表增删改查)
前言 大家在做业务系统的时候,很多地方都是列表增删改查,做这些功能占据了大家很长时间,如果我们有类似的业务,半个小时就能做出一套那是不是很爽呢. 这样我们就可以有更多的时间学习一些新的东西.我们这套框 ...
- Spring Boot 2 实践记录之 封装依赖及尽可能不创建静态方法以避免在 Service 和 Controller 的单元测试中使用 Powermock
在前面的文章中(Spring Boot 2 实践记录之 Powermock 和 SpringBootTest)提到了使用 Powermock 结合 SpringBootTest.WebMvcTest ...
- CMake学习记录--list(列表操作命令)
CMake是一个跨平台的工程管理工具,能方便的把工程转换为vs各个版本.Borland Makefiles.MSSYS Makefiles.NMake Makefiles等工程,对于经常在不同IDE下 ...
- 学习记录(Python列表)
列表(List)是Python语言中最通用的序列数据结构之一,列表是一个没有固定长度的,用来表示任意类型对象的位置相关的有序集合.列表中的数据项不需要具有相同的数据类型 列表的基本操作: 1.创建列表 ...
- 使用JDBC获取数据库中的一条记录并封装为Bean
比如我数据库中存入的是一条一条的用户信息,现在想取出一个人的个人信息,并封装为Bean对象,可以使用queryForObject来获取数据并通过new BeanPropertyRowMapper(Be ...
- <记录> curl 封装函数
1. POST请求 参数1 : 请求地址 参数2 : 数组形式的参数 /** * @param string $url post请求地址 * @param array $params * @retur ...
随机推荐
- Power BI 10 DAY
Power BI 上下文 筛选上下文只管筛选,行上下文只管迭代,两者互不干涉,各司其职 计算列操作会自动创建行上下文 度量值不会自动创建行上下文,需要人为使用迭代函数进行行上下文的创建 (迭代函数本身 ...
- 使用mail发送邮件
使用mail发送邮件 只做个简单介绍,使用场景不多 1.安装mail yum install mailx -y 2.配置 1)配置文件在/etc/mail.rc,有兴趣的可以仔细读一下默认的配 ...
- Git将某个文件合并到指定分支
企业开发中,经常会单独拉分支去做自己的需求开发,但是某些时候一些公共的配置我们需要从主线pull,这时候整个分支merge显然不合适 1.切换至待合并文件的分支 git checkout <br ...
- 大白菜方式制作win10 PE启动U盘
说明 最近帮朋友安装下win10,用了2种制作U盘启动盘的方式.记录一下也方便大家少走弯路. 准备的工具: 1.大白菜软件 2.win10镜像 3.1个U盘,U盘容量 > 8G即可. 制作PE启 ...
- cf796部分题解
C. Manipulating History 题意:给出一些字符串,有原始串(只含一个字符的串).被替换的串.替换串. 最终串(最后一行),求原始串. 2 a ab b cd acd Initial ...
- collection.abc模块下的抽象基类UML类图说明
说明 Iterable.Container和Sized 每个容器都应该继承这三个抽象基类,或者实现兼容的协议.Iterable通过__iter__方法支持迭代, Container通过__contai ...
- render_to_string快捷函数,渲染模板字符串
# views.py from django.template.loader import render_to_string from django.http import HttpResponse ...
- Java纯手打web服务器(三)
概要:考虑到上一篇中的request和response对象的安全性,不允许servlt程序员在service方法中把servletRequest和servletResponse对象进行强转reques ...
- 如何使用 perf 分析 splice 中 pipe 的容量变化
如何使用 perf 分析 splice 中 pipe 的容量变化 这个文章为了填上一篇文章的坑的,跟踪内核函数本来是准备使用 ebpf 的,但是涉及到了低内核版本,只能使用 kprobe 了. 恰好, ...
- 俄罗斯套娃 (Matryoshka) 嵌入模型概述
在这篇博客中,我们将向你介绍俄罗斯套娃嵌入的概念,并解释为什么它们很有用.我们将讨论这些模型在理论上是如何训练的,以及你如何使用 Sentence Transformers 来训练它们. 除此之外,我 ...