记录--这样封装列表 hooks,一天可以开发 20 个页面

news2024/9/24 3:20:04

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

这样封装列表 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 中提供了 onPullDownRefreshonReachBottom钩子,在其中处理相关逻辑即可
  • 有些列表可能不是在页面中,而是在 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

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/187902.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java 面试题合集】ThreadPoolExecutor 线程池面试题

文章目录自定义的线程池的 7 个参数如何合理设置核心线程数 corePoolSize 的大小《JAVA 并发编程实战》中的方案java 开发手册中为什么不允许使用 Executors 默认的实现?一个线程池中的线程异常了,那么线程池会怎么处理这个线程?线程池被创建后里面有线…

OpenCV——总结《图像处理-1》

1.HSV H - 色调(主波长)。S - 饱和度(纯度/颜色的阴影)。V值(强度) hsvcv2.cvtColor(img,cv2.COLOR_BGR2HSV)2.图像阈值 函数介绍: ret, dst cv2.threshold(src, thresh, maxval, type) sr…

嵌入式Linux从入门到精通之第九节:系统编程

系统编程概述 在讲解系统编程之前,先了解几个概念: 操作系统的作用: 操作系统用来管理所有的资源,并将不同的设备和不同的程序关联起来。 什么是Linux系统编程? 在有操作系统的环境下编程,并使用操作系统提供的系统调用及各种库,对系统资源进行访问。 学会了C语言再知…

Grafana9.3.x在windows上的安装及使用

Grafana9.3.x的安装及使用1. Grafana install1.1 Download1.2 Install2. User Guide1.1 Document1.2 Table视图背景色渲染3.Awakening1. Grafana install 1.1 Download 下载地址 Grafana Website: https://grafana.com/. 1.2 Install 直接点击安装就好了 进入conf目录复制一…

python集合语法与应用

python集合语法与应用 文章目录python集合语法与应用一、实验目的二、实验原理三、实验环境四、实验内容五、实验步骤1.创建2.增加3.删除4.集合运算5.拓展知识一6.拓展知识二总结一、实验目的 掌握集合的用法 二、实验原理 集合中只能包含数字、字符串、元组等不可变的类型的…

规则引擎,实现业务低代码开发的重要工具

规则引擎,是将业务执行抽象化的配置,通过其定义的数据结构、算法和流程来实现应用程序功能的普适化。 规则引擎可以帮助企业提高业务开发效率,提高运营的灵活性,降低运营成本与开发成本,让系统更加智能化灵活化。这里以…

【Rust】5. 所有权

5. 所有权 5.1 什么是所有权 5.1.1 栈(Stack)与堆(Heap) 5.1.2 所有权规则 5.1.3 变量作用域 5.1.4 String 类型 String 类型可进行修改,而字符串字面值是不可以的!(区别在于二者对内存的处理…

OAuth2

目录一、什么是OAuth2.0二、OAuth2中的角色三、认证流程四、生活中的Oauth2思维5. 令牌的特点6.OAuth2授权方式6.1 授权码6.2 隐藏方式6.3 密码方式6.4 凭证方式一、什么是OAuth2.0 OAuth2.0是目前使用非常广泛的授权机制,用于授权第三方应用获取用户的数据。 举例…

PythonWeb Django框架学习笔记

文章目录Django一、初步了解Django1.1 创建项目1.2 文件介绍1.3 APP的创建和说明添加新的app注册app创建页面1.4 templates模板templates语法单一变量列表循环【列表】字典循环【字典】列表套字典条件判断templates小结1.5 请求和响应案例:用户管理二、数据库操作2.…

前言技术之mybatis-plus 01

目录 1.什么是mybatis-plus 2.初体验 3.日志 4.主键生成策略 5.更新 6.自动填充 1.什么是mybatis-plus 升级版的mybatis,目的是让mybatis更易于使用, 用官方的话说“为简化而生” 官网: MyBatis-Plus 2.初体验 1.准备数据库脚本 数据…

再获殊荣!天云数据入选第一批北京市级企业技术中心,Hubble数据库提供新一代信息技术科技服务

为助力北京国际科技创新中心建设,贯彻落实北京市“十四五”时期高精尖产业发展规划,引导和支持企业加强创新能力,培育和引导企业技术中心建设,北京市经济和信息化局组织开展了2022年度第一批北京市市级企业技术中心的创建工作&…

【Python学习笔记】5. Python3 基本数据类型(上)——数值型、字符串型

前言 Python 中的变量不需要声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。 Python3 基本数据类型 Python 中的变量不需要声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。 在 Python 中,变量就是变…

tomcat配置多个host,并且避免重复加载

目录 1.实验描述 2.实验环境 2.实验过程 2.1 创建order和user项目 2.2 打包项目 2.3 修改tomcat的server.xml的配置 2.4 启动tomcat 2.5 配置本地host 2.6 验证 1.实验描述 目前有两个域名,分别是: order.abc.com user.abc.com 两个项目&…

微信小程序学习第1天:微信小程序开发入门介绍

前言:微信小程序开发模式 1、申请小程序开发账号 2、安装小程序开发者工具 3、创建和配置小程序项目 一、申请小程序开发账号 1、体验小程序 2、注册小程序开发账号 使用浏览器打开https://mp.weixin.qq.com网址,按照提示注册 注册承购后&#xff0c…

微服务 初始 分布式搜索引擎 Elastic Search

文章目录⛄引言一、什么是Elastic Search?二、Elastic Search 倒排索引⛅正向索引⚡倒排索引⛄正向和倒排三、ES的一些概念⛅文档和字段⚡索引和映射四、MySQL 与 Elasticsearch⛵小结⛄引言 本文参考黑马 分布式Elastic search Elasticsearch是一款非常强大的开源…

OBB的计算python实现

OBB的计算python实现1. 实现步骤步骤① 分解点集的xyz分量步骤② 对x、y、z这三个随机变量(一维数组)求协方差矩阵步骤③ 对步骤②中的协方差矩阵求解特征值与特征向量,特征向量构造列向量矩阵M步骤④ 将点集的几何中心平移至坐标系原点&…

最全java面试题及答案(208道)

本文分为十九个模块,分别是:「Java 基础、容器、多线程、反射、对象拷贝、Java Web 、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、MyBatis、RabbitMQ、Kafka、Zookeeper、MySQL、Redis、JVM」 ,如下图所…

Discord怎么抢白名单?Discord多账号防关联可以提高白名单成功率

Discord作为一个社交媒体,在加密货币领域扮演着重要的角色,它强大而便捷。更重要的是,如果你是一个投资者,Discord无疑是你了解项目动态,深入社区沟通交流的重要工具。 相信很多玩NFT和Web3的朋友都不会对discord 陌生…

分位数的理解与查询

目录 分位数的定义与理解: pyspark 中分位数查询 方式1 : 某列分位数查询: 采用dataframe的approxQuantile属性,返回一个list 例子: 方式2 : 转换为toPandas,利用pandas.dataframe利的…

全志T113-i+玄铁HiFi4开发板硬件说明书(2)

前 言 本文档主要介绍开发板硬件接口资源以及设计注意事项等内容,测试板卡为全志T113-i+玄铁HiFi4开发板,由于篇幅问题,本篇文章共分为上下两集,点击账户可查看更多内容详情,开发问题欢迎留言,感谢关注。 T113-i处理器的IO电平标准一般为1.8V和3.3V,上拉电源一般不超过…