記錄--這樣封裝列表 hooks,一天可以開發 20 個頁面

来源:https://www.cnblogs.com/smileZAZ/archive/2023/01/30/17076271.html
-Advertisement-
Play Games

這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 這樣封裝列表 hooks,一天可以開發 20 個頁面 前言 在做移動端的需求時,我們經常會開發一些列表頁,這些列表頁大多數有著相似的功能:分頁獲取列表、上拉載入、下拉刷新··· 在 Vue 出來 compositionAPI 之前,我們想 ...


這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

這樣封裝列表 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

如果對您有所幫助,歡迎您點個關註,我會定時更新技術文檔,大家一起討論學習,一起進步。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一:背景 1. 講故事 年前遇到了好幾例托管堆被損壞的案例,有些運氣好一些,從被破壞的托管堆記憶體現場能觀測出大概是什麼問題,但更多的情況下是無法做出準確判斷的,原因就在於生成的dump是第二現場,借用之前文章的一張圖,大家可以理解一下。 為了幫助更多受此問題困擾的朋友,這篇來整理一下如何 快狠準 的 ...
  • 1.背景知識 CRLF用來表示文本換行的方式 ,CR是回車的意思,對應 \r ;LF 是換行的意思,對應 \n Windows 換行符是 \r\n Unix 換行是 \n 如果一個將要在Linux伺服器上使用的文件,比如某個配置文件,在windows電腦打開,編輯過,那麼文件每一行末尾會多有\r 。 ...
  • 一:什麼是乙太網: 乙太網是一種區域網技術,乙太網是一種用於數據鏈路層的協議類型。 二:乙太網的由來: 乙太網是美國施樂(Xerox)公司的Palo Alto研究中心(簡稱為PARC)於1975年研製成功的。那時乙太網是一種基帶匯流排區域網,當時的數據率為2.94 Mbit/s。乙太網用無源電纜作為總 ...
  • 網卡 網卡,又叫做通信適配器(adapter),早期的時候是插在機箱裡面的網路介面板,這種介面板又稱為網路介面卡NIC(Network Interface Card)或簡稱為“網卡”。因為後面集成到主板上了,所以又叫做通信適配器(adapter)。 網卡的作用: 電腦和外界區域網進行連接是通過通信 ...
  • 一、背景 二、概述 三、下線流程與原理 1.讀取待下線節點列表 2.判斷節點下線模式 3.設置超時時間 4.RMNode 處理下線事件 5.監控節點的狀態、下線節點 四、相關的Yarn集群配置 一、背景 接手部門 Hadoop 和 Flink 集群半年了,一直忙著上雲的事兒,很少有時間去琢磨運維的事 ...
  • 前言 SELinux是什麼 安全增強型 Linux(SELinux)是一種採用安全架構的 Linux® 系統,它能夠讓管理員更好地管控哪些人可以訪問系統。它最初是作為 Linux 內核的一系列補丁,由美國國家安全局(NSA)利用 Linux 安全模塊(LSM)開發而成。 SELinux工作原理 SE ...
  • MySQL具有開源免費,運維簡單,性能好等優點,是在汽車之家使用最多的一種資料庫。資料庫作為應用的後端存儲,承擔著數據持久化存儲的功能,是應用可以正常對外提供服務的關鍵組件,資料庫的高可用非常重要。 相對於成熟的商業資料庫軟體,開源的 MySQL高可用需要使用者自己進行設計和研發,本文介紹汽車... ...
  • Flutter 3.7 發佈,本人對其中後臺 isolate 通道比較感興趣,迫不及待翻譯了下Aaron Clarke文章,第一次翻譯,有不足地方歡迎各位大佬們評論區指正,我將持續更新到本文,謝謝。 原文地址:Introducing background isolate channels | by ...
一周排行
    -Advertisement-
    Play Games
  • 1. 說明 /* Performs operations on System.String instances that contain file or directory path information. These operations are performed in a cross-pla ...
  • 視頻地址:【WebApi+Vue3從0到1搭建《許可權管理系統》系列視頻:搭建JWT系統鑒權-嗶哩嗶哩】 https://b23.tv/R6cOcDO qq群:801913255 一、在appsettings.json中設置鑒權屬性 /*jwt鑒權*/ "JwtSetting": { "Issuer" ...
  • 引言 集成測試可在包含應用支持基礎結構(如資料庫、文件系統和網路)的級別上確保應用組件功能正常。 ASP.NET Core 通過將單元測試框架與測試 Web 主機和記憶體中測試伺服器結合使用來支持集成測試。 簡介 集成測試與單元測試相比,能夠在更廣泛的級別上評估應用的組件,確認多個組件一起工作以生成預 ...
  • 在.NET Emit編程中,我們探討了運算操作指令的重要性和應用。這些指令包括各種數學運算、位操作和比較操作,能夠在動態生成的代碼中實現對數據的處理和操作。通過這些指令,開發人員可以靈活地進行算術運算、邏輯運算和比較操作,從而實現各種複雜的演算法和邏輯......本篇之後,將進入第七部分:實戰項目 ...
  • 前言 多表頭表格是一個常見的業務需求,然而WPF中卻沒有預設實現這個功能,得益於WPF強大的控制項模板設計,我們可以通過修改控制項模板的方式自己實現它。 一、需求分析 下圖為一個典型的統計表格,統計1-12月的數據。 此時我們有一個需求,需要將月份按季度劃分,以便能夠直觀地看到季度統計數據,以下為該需求 ...
  • 如何將 ASP.NET Core MVC 項目的視圖分離到另一個項目 在當下這個年代 SPA 已是主流,人們早已忘記了 MVC 以及 Razor 的故事。但是在某些場景下 SSR 還是有意想不到效果。比如某些靜態頁面,比如追求首屏載入速度的時候。最近在項目中回歸傳統效果還是不錯。 有的時候我們希望將 ...
  • System.AggregateException: 發生一個或多個錯誤。 > Microsoft.WebTools.Shared.Exceptions.WebToolsException: 生成失敗。檢查輸出視窗瞭解更多詳細信息。 內部異常堆棧跟蹤的結尾 > (內部異常 #0) Microsoft ...
  • 引言 在上一章節我們實戰了在Asp.Net Core中的項目實戰,這一章節講解一下如何測試Asp.Net Core的中間件。 TestServer 還記得我們在集成測試中提供的TestServer嗎? TestServer 是由 Microsoft.AspNetCore.TestHost 包提供的。 ...
  • 在發現結果為真的WHEN子句時,CASE表達式的真假值判斷會終止,剩餘的WHEN子句會被忽略: CASE WHEN col_1 IN ('a', 'b') THEN '第一' WHEN col_1 IN ('a') THEN '第二' ELSE '其他' END 註意: 統一各分支返回的數據類型. ...
  • 在C#編程世界中,語法的精妙之處往往體現在那些看似微小卻極具影響力的符號與結構之中。其中,“_ =” 這一組合突然出現還真不知道什麼意思。本文將深入剖析“_ =” 的含義、工作原理及其在實際編程中的廣泛應用,揭示其作為C#語法奇兵的重要角色。 一、下劃線 _:神秘的棄元符號 下劃線 _ 在C#中並非 ...