日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Vue3的組合API如何請求數(shù)據(jù)?

前言

成都創(chuàng)新互聯(lián)公司專注于企業(yè)成都營銷網(wǎng)站建設、網(wǎng)站重做改版、徐聞網(wǎng)站定制設計、自適應品牌網(wǎng)站建設、HTML5建站、商城網(wǎng)站制作、集團公司官網(wǎng)建設、外貿網(wǎng)站建設、高端網(wǎng)站制作、響應式網(wǎng)頁設計等建站業(yè)務,價格優(yōu)惠性價比高,為徐聞等各大城市提供網(wǎng)站開發(fā)制作服務。

之前在學習 React Hooks 的過程中,看到一篇外網(wǎng)文章,通過 Hooks 來請求數(shù)據(jù),并將這段邏輯抽象成一個新的 Hooks 給其他組件復用,我也在我的博客里翻譯了一下:《在 React Hooks 中如何請求數(shù)據(jù)?》,感興趣可以看看。雖然是去年的文章,在閱讀之后一下子就掌握了 Hooks 的使用方式,而且數(shù)據(jù)請求是在業(yè)務代碼中很常用的邏輯。

Vue 3 已經(jīng)發(fā)布一段時間了,其組合 API 多少有點 React Hooks 的影子在里面,今天我也打算通過這種方式來學習下組合 API。

項目初始化

為了快速啟動一個 Vue 3 項目,我們直接使用當下最熱門的工具 Vite 來初始化項目。整個過程一氣呵成,行云流水。

 
 
 
 
  1. npm init vite-app vue3-app
 
 
 
 
  1. # 打開生成的項目文件夾
  2. cd vue3-app
  3. # 安裝依賴
  4. npm install
  5. # 啟動項目
  6. npm run dev

我們打開 App.vue 將生成的代碼先刪掉。

組合 API 的入口

接下來我們將通過 Hacker News API 來獲取一些熱門文章,Hacker News API返回的數(shù)據(jù)結構如下:

 
 
 
 
  1. {
  2.   "hits": [
  3.     {
  4.       "objectID": "24518295",
  5.       "title": "Vue.js 3",
  6.       "url": "https://github.com/vuejs/vue-next/releases/tag/v3.0.0",
  7.     },
  8.     {...},
  9.     {...},
  10.   ]
  11. }

我們通過 ui > li 將新聞列表展示到界面上,新聞數(shù)據(jù)從 hits 遍歷中獲取。

 
 
 
 

在講解數(shù)據(jù)請求前,我看先看看 setup() 方法,組合 API 需要通過 setup() 方法來啟動,setup() 返回的數(shù)據(jù)可以在模板內使用,可以簡單理解為 Vue 2 里面 data() 方法返回的數(shù)據(jù),不同的是,返回的數(shù)據(jù)需要先經(jīng)過 reactive() 方法進行包裹,將數(shù)據(jù)變成響應式。

組合 API 中請求數(shù)據(jù)

在 Vue 2 中,我們請求數(shù)據(jù)時,通常需要將發(fā)起請求的代碼放到某個生命周期中(created或 mounted)。在 setup() 方法內,我們可以使用 Vue 3 提供的生命周期鉤子將請求放到特定生命周期內,關于生命周期鉤子方法與之前生命周期的對比如下:

生命周期

可以看到,基本上就是在之前的方法名前加上了一個 on,且并沒有提供 onCreated 的鉤子,因為在 setup() 內執(zhí)行就相當于在 created 階段執(zhí)行。下面我們在 mounted 階段來請求數(shù)據(jù):

 
 
 
 
  1. import { reactive, onMounted } from 'vue'
  2. export default {
  3.   setup() {
  4.     const state = reactive({
  5.       hits: []
  6.     })
  7.     onMounted(async () => {
  8.       const data = await fetch(
  9.         'https://hn.algolia.com/api/v1/search?query=vue'
  10.       ).then(rsp => rsp.json())
  11.       state.hits = data.hits
  12.     })
  13.     return state
  14.   }
  15. }

最后效果如下:

Demo

監(jiān)聽數(shù)據(jù)變動

Hacker News 的查詢接口有一個 query 參數(shù),前面的案例中,我們將這個參數(shù)固定了,現(xiàn)在我們通過響應式的數(shù)據(jù)來定義這個變量。

 
 
 
 

現(xiàn)在我們在輸入框修改,就能觸發(fā) state.query 同步更新,但是并不會觸發(fā) fetch 重新調用,所以我們需要通過 watchEffect() 來監(jiān)聽響應數(shù)據(jù)的變化。

 
 
 
 
  1. import { reactive, onMounted, watchEffect } from 'vue'
  2. export default {
  3.   setup() {
  4.     const state = reactive({
  5.       query: 'vue',
  6.       hits: []
  7.     })
  8.     const fetchData = async (query) => {
  9.       const data = await fetch(
  10.         `https://hn.algolia.com/api/v1/search?query=${query}`
  11.       ).then(rsp => rsp.json())
  12.       state.hits = data.hits
  13.     }
  14.     onMounted(() => {
  15.       fetchData(state.query)
  16.       watchEffect(() => {
  17.         fetchData(state.query)
  18.       })
  19.     })
  20.     return state
  21.   }
  22. }

由于 watchEffect() 首次調用的時候,其回調就會執(zhí)行一次,造成初始化時會請求兩次接口,所以我們需要把 onMounted 中的 fetchData 刪掉。

 
 
 
 
  1. onMounted(() => {
  2. - fetchData(state.query)
  3.   watchEffect(() => {
  4.     fetchData(state.query)
  5.   })
  6. })

Demo

watchEffect() 會監(jiān)聽傳入函數(shù)內所有的響應式數(shù)據(jù),一旦其中的某個數(shù)據(jù)發(fā)生變化,函數(shù)就會重新執(zhí)行。如果要取消監(jiān)聽,可以調用 watchEffect() 的返回值,它的返回值為一個函數(shù)。下面舉個例子:

 
 
 
 
  1. const stop = watchEffect(() => {
  2.   if (state.query === 'vue3') {
  3.     // 當 query 為 vue3 時,停止監(jiān)聽
  4.     stop()
  5.   }
  6.   fetchData(state.query)
  7. })

當我們在輸入框輸入 "vue3" 后,就不會再發(fā)起請求了。

Demo

返回事件方法

現(xiàn)在有個問題就是 input 內的值每次修改都會觸發(fā)一次請求,我們可以增加一個按鈕,點擊按鈕后再觸發(fā) state.query 的更新。

 
 
 
 

可以注意到 button 綁定的 click 事件的方法,也是通過 setup() 方法返回的,我們可以將 setup() 方法返回值理解為 Vue2 中 data() 方法和 methods 對象的合并。

原先的返回值 state 變成了現(xiàn)在返回值的一個屬性,所以我們在模板層取數(shù)據(jù)的時候,需要進行一些修改,在前面加上 state.。

 
 
 
 

Demo

返回數(shù)據(jù)

修改作為強迫癥患者,在模板層通過 state.xxx 的方式獲取數(shù)據(jù)實在是難受,那我們是不是可以通過對象解構的方式將 state 的數(shù)據(jù)返回呢?

 
 
 
 

答案是『不可以』。修改代碼后,可以看到頁面雖然發(fā)起了請求,但是頁面并沒有展示數(shù)據(jù)。

state 在解構后,數(shù)據(jù)就變成了靜態(tài)數(shù)據(jù),不能再被跟蹤,返回值類似于:

 
 
 
 
  1. export default {
  2.   setup(props, ctx) {
  3.     // 省略部分代碼...
  4.     return {
  5.       input: 'vue',
  6.       query: 'vue',
  7.       hits: [],
  8.       setQuery,
  9.     }
  10.   }
  11. }

Demo

為了跟蹤基礎類型的數(shù)據(jù)(即非對象數(shù)據(jù)),Vue3 也提出了解決方案:ref() 。

 
 
 
 
  1. import { ref } from 'vue'
  2. const count = ref(0)
  3. console.log(count.value) // 0
  4. count.value++
  5. console.log(count.value) // 1

上面為 Vue 3 的官方案例,ref() 方法返回的是一個對象,無論是修改還是獲取,都需要取返回對象的 value 屬性。

我們將 state 從響應對象改為一個普通對象,然后所有屬性都使用 ref 包裹,這樣修改后,后續(xù)的解構才做才能生效。這樣的弊端就是,state 的每個屬性在修改時,都必須取其value 屬性。但是在模板中不需要追加 .value,Vue 3 內部有對其進行處理。

 
 
 
 
  1. import { ref, onMounted, watchEffect } from 'vue'
  2. export default {
  3.   setup() {
  4.     const state = {
  5.       input: ref('vue'),
  6.       query: ref('vue'),
  7.       hits: ref([])
  8.     }
  9.     const fetchData = async (query) => {
  10.       const data = await fetch(
  11.         `https://hn.algolia.com/api/v1/search?query=${query}`
  12.       ).then(rsp => rsp.json())
  13.       state.hits.value = data.hits
  14.     }
  15.     onMounted(() => {
  16.       watchEffect(() => {
  17.         fetchData(state.query.value)
  18.       })
  19.     })
  20.     const setQuery = () => {
  21.       state.query.value = state.input.value
  22.     }
  23.     return {
  24.       ...state,
  25.       setQuery,
  26.     }
  27.   }
  28. }

有沒有辦法保持 state 為響應對象,同時又支持其對象解構的呢?當然是有的,Vue 3 也提供了解決方案:toRefs() 。toRefs() 方法可以將一個響應對象變?yōu)槠胀▽ο?,并且給每個屬性加上 ref()。

 
 
 
 
  1. import { toRefs, reactive, onMounted, watchEffect } from 'vue'
  2. export default {
  3.   setup() {
  4.     const state = reactive({
  5.       input: 'vue',
  6.       query: 'vue',
  7.       hits: []
  8.     })
  9.     const fetchData = async (query) => {
  10.       const data = await fetch(
  11.         `https://hn.algolia.com/api/v1/search?query=${query}`
  12.       ).then(rsp => rsp.json())
  13.       state.hits = data.hits
  14.     }
  15.     onMounted(() => {
  16.       watchEffect(() => {
  17.         fetchData(state.query)
  18.       })
  19.     })
  20.     const setQuery = () => {
  21.       state.query = state.input
  22.     }
  23.     return {
  24.       ...toRefs(state),
  25.       setQuery,
  26.     }
  27.   }
  28. }

Loading 與 Error 狀態(tài)

通常,我們發(fā)起請求的時候,需要為請求添加 Loading 和 Error 狀態(tài),我們只需要在 state中添加兩個變量來控制這兩種狀態(tài)即可。

 
 
 
 
  1. export default {
  2.   setup() {
  3.     const state = reactive({
  4.       input: 'vue',
  5.       query: 'vue',
  6.       hits: [],
  7.       error: false,
  8.       loading: false,
  9.     })
  10.     const fetchData = async (query) => {
  11.       state.error = false
  12.       state.loading = true
  13.       try {
  14.         const data = await fetch(
  15.           `https://hn.algolia.com/api/v1/search?query=${query}`
  16.         ).then(rsp => rsp.json())
  17.         state.hits = data.hits
  18.       } catch {
  19.         state.error = true
  20.       }
  21.       state.loading = false
  22.     }
  23.     onMounted(() => {
  24.       watchEffect(() => {
  25.         fetchData(state.query)
  26.       })
  27.     })
  28.     const setQuery = () => {
  29.       state.query = state.input
  30.     }
  31.     return {
  32.       ...toRefs(state),
  33.       setQuery,
  34.     }
  35.   }
  36. }

同時在模板使用這兩個變量:

 
 
 
 

展示 Loading、Error 狀態(tài):

Demo

將數(shù)據(jù)請求邏輯抽象

用過 umi 的同學肯定知道 umi 提供了一個叫做 useRequest 的 Hooks,用于請求數(shù)據(jù)非常的方便,那么我們通過 Vue 的組合 API 也可以抽象出一個類似于 useRequest 的公共方法。

接下來我們新建一個文件 useRequest.js :

 
 
 
 
  1. import {
  2.   toRefs,
  3.   reactive,
  4. } from 'vue'
  5. export default (options) => {
  6.   const { url } = options
  7.   const state = reactive({
  8.     data: {},
  9.     error: false,
  10.     loading: false,
  11.   })
  12.   const run = async () => {
  13.     state.error = false
  14.     state.loading = true
  15.     try {
  16.       const result = await fetch(url).then(res => res.json())
  17.       state.data = result
  18.     } catch(e) {
  19.       state.error = true
  20.     }
  21.     state.loading = false
  22.   }
  23.   return {
  24.     run,
  25.     ...toRefs(state)
  26.   }
  27. }

然后在 App.vue 中引入:

 
 
 
 

當前的 useRequest 還有兩個缺陷:

1.傳入的 url 是固定的,query 修改后,不能及時的反應到 url 上;

2.不能自動請求,需要手動調用一下 run 方法;

 
 
 
 
  1. import {
  2.   isRef,
  3.   toRefs,
  4.   reactive,
  5.   onMounted,
  6. } from 'vue'
  7. export default (options) => {
  8.   const { url, manual = false, params = {} } = options
  9.   const state = reactive({
  10.     data: {},
  11.     error: false,
  12.     loading: false,
  13.   })
  14.   const run = async () => {
  15.     // 拼接查詢參數(shù)
  16.     let query = ''
  17.     Object.keys(params).forEach(key => {
  18.       const val = params[key]
  19.       // 如果去 ref 對象,需要取 .value 屬性
  20.       const value = isRef(val) ? val.value : val
  21.       query += `${key}=${value}&`
  22.     })
  23.     state.error = false
  24.     state.loading = true
  25.     try {
  26.       const result = await fetch(`${url}?${query}`)
  27.        .then(res => res.json())
  28.       state.data = result
  29.     } catch(e) {
  30.       state.error = true
  31.     }
  32.     state.loading = false
  33.   }
  34.   onMounted(() => {
  35.     // 第一次是否需要手動調用
  36.     !manual && run()
  37.   })
  38.   return {
  39.     run,
  40.     ...toRefs(state)
  41.   }
  42. }

經(jīng)過修改后,我們的邏輯就變得異常簡單了。

 
 
 
 
  1. import useRequest from './useRequest'
  2. export default {
  3.   setup() {
  4.     const query = ref('vue')
  5.     const { data, loading, error, run } = useRequest(
  6.       {
  7.         url: 'https://hn.algolia.com/api/v1/search',
  8.         params: {
  9.           query
  10.         }
  11.       }
  12.     )
  13.     return {
  14.       data,
  15.       query,
  16.       error,
  17.       loading,
  18.       search: run,
  19.     }
  20.   }
  21. }

當然,這個 useRequest 還有很多可以完善的地方,例如:不支持 http 方法修改、不支持節(jié)流防抖、不支持超時時間等等。最后,希望大家看完文章后能有所收獲。

本文轉載自微信公眾號「更了不起的前端」,可以通過以下二維碼關注。轉載本文請聯(lián)系更了不起的前端公眾號。


文章標題:Vue3的組合API如何請求數(shù)據(jù)?
網(wǎng)站URL:http://www.5511xx.com/article/djchgcg.html