這篇文章主要講解了“Vue 3的組合API怎么請求數(shù)據(jù)”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Vue 3的組合API怎么請求數(shù)據(jù)”吧!
十載專注建站、設(shè)計、互聯(lián)網(wǎng)產(chǎn)品定制設(shè)計服務(wù),業(yè)務(wù)涵蓋成都品牌網(wǎng)站建設(shè)、商城網(wǎng)站制作、微信小程序定制開發(fā)、軟件系統(tǒng)開發(fā)、重慶APP開發(fā)公司等。憑借多年豐富的經(jīng)驗,我們會仔細(xì)了解每個客戶的需求而做出多方面的分析、設(shè)計、整合,為客戶設(shè)計出具風(fēng)格及創(chuàng)意性的商業(yè)解決方案,創(chuàng)新互聯(lián)建站更提供一系列網(wǎng)站制作和網(wǎng)站推廣的服務(wù),以推動各中小企業(yè)全面信息數(shù)字化,并利用創(chuàng)新技術(shù)幫助各行業(yè)提升企業(yè)形象和運營效率。
項目初始化
為了快速啟動一個 Vue 3 項目,我們直接使用當(dāng)下最熱門的工具 Vite 來初始化項目。整個過程一氣呵成,行云流水。
npm init vite-app vue3-app
# 打開生成的項目文件夾 cd vue3-app # 安裝依賴 npm install # 啟動項目 npm run dev
我們打開 App.vue 將生成的代碼先刪掉。
組合 API 的入口
接下來我們將通過 Hacker News API 來獲取一些熱門文章,Hacker News API返回的數(shù)據(jù)結(jié)構(gòu)如下:
{ "hits": [ { "objectID": "24518295", "title": "Vue.js 3", "url": "https://github.com/vuejs/vue-next/releases/tag/v3.0.0", }, {...}, {...}, ] }
我們通過 ui > li 將新聞列表展示到界面上,新聞數(shù)據(jù)從 hits 遍歷中獲取
<template> <ul> <li v-for="item of hits" :key="item.objectID" > <a :href="item.url">{{item.title}}</a> </li> </ul> </template> <script> import { reactive } from 'vue' export default { setup() { const state = reactive({ hits: [] }) return state } } </script>
在講解數(shù)據(jù)請求前,我看先看看 setup() 方法,組合 API 需要通過 setup() 方法來啟動,setup() 返回的數(shù)據(jù)可以在模板內(nèi)使用,可以簡單理解為 Vue 2 里面 data() 方法返回的數(shù)據(jù),不同的是,返回的數(shù)據(jù)需要先經(jīng)過 reactive() 方法進(jìn)行包裹,將數(shù)據(jù)變成響應(yīng)式。
組合 API 中請求數(shù)據(jù)
在 Vue 2 中,我們請求數(shù)據(jù)時,通常需要將發(fā)起請求的代碼放到某個生命周期中(created 或 mounted)。在 setup() 方法內(nèi),我們可以使用 Vue 3 提供的生命周期鉤子將請求放到特定生命周期內(nèi),關(guān)于生命周期鉤子方法與之前生命周期的對比如下:
生命周期
可以看到,基本上就是在之前的方法名前加上了一個 on,且并沒有提供 onCreated 的鉤子,因為在 setup() 內(nèi)執(zhí)行就相當(dāng)于在 created 階段執(zhí)行。下面我們在 mounted 階段來請求數(shù)據(jù):
import { reactive, onMounted } from 'vue' export default { setup() { const state = reactive({ hits: [] }) onMounted(async () => { const data = await fetch( 'https://hn.algolia.com/api/v1/search?query=vue' ).then(rsp => rsp.json()) state.hits = data.hits }) return state } }
最后效果如下:
監(jiān)聽數(shù)據(jù)變動
Hacker News 的查詢接口有一個 query 參數(shù),前面的案例中,我們將這個參數(shù)固定了,現(xiàn)在我們通過響應(yīng)式的數(shù)據(jù)來定義這個變量。
<template> <input type="text" v-model="query" /> <ul> <li v-for="item of hits" :key="item.objectID" > <a :href="item.url">{{item.title}}</a> </li> </ul> </template> <script> import { reactive, onMounted } from 'vue' export default { setup() { const state = reactive({ query: 'vue', hits: [] }) onMounted((async () => { const data = await fetch( `https://hn.algolia.com/api/v1/search?query=${state.query}` ).then(rsp => rsp.json()) state.hits = data.hits }) return state } } </script>
現(xiàn)在我們在輸入框修改,就能觸發(fā) state.query 同步更新,但是并不會觸發(fā) fetch 重新調(diào)用,所以我們需要通過 watchEffect() 來監(jiān)聽響應(yīng)數(shù)據(jù)的變化。
import { reactive, onMounted, watchEffect } from 'vue' export default { setup() { const state = reactive({ query: 'vue', hits: [] }) const fetchData = async (query) => { const data = await fetch( `https://hn.algolia.com/api/v1/search?query=${query}` ).then(rsp => rsp.json()) state.hits = data.hits } onMounted(() => { fetchData(state.query) watchEffect(() => { fetchData(state.query) }) }) return state } }
由于 watchEffect() 首次調(diào)用的時候,其回調(diào)就會執(zhí)行一次,造成初始化時會請求兩次接口,所以我們需要把 onMounted 中的 fetchData 刪掉。
onMounted(() => { - fetchData(state.query) watchEffect(() => { fetchData(state.query) }) })
watchEffect() 會監(jiān)聽傳入函數(shù)內(nèi)所有的響應(yīng)式數(shù)據(jù),一旦其中的某個數(shù)據(jù)發(fā)生變化,函數(shù)就會重新執(zhí)行。如果要取消監(jiān)聽,可以調(diào)用 watchEffect() 的返回值,它的返回值為一個函數(shù)。下面舉個例子:
const stop = watchEffect(() => { if (state.query === 'vue3') { // 當(dāng) query 為 vue3 時,停止監(jiān)聽 stop() } fetchData(state.query) })
當(dāng)我們在輸入框輸入 "vue3" 后,就不會再發(fā)起請求了。
返回事件方法
現(xiàn)在有個問題就是 input 內(nèi)的值每次修改都會觸發(fā)一次請求,我們可以增加一個按鈕,點擊按鈕后再觸發(fā) state.query 的更新。
<template> <input type="text" v-model="input" /> <button @click="setQuery">搜索</button> <ul> <li v-for="item of hits" :key="item.objectID" > <a :href="item.url">{{item.title}}</a> </li> </ul> </template> <script> import { reactive, onMounted, watchEffect } from 'vue' export default { setup() { const state = reactive({ input: 'vue', query: 'vue', hits: [] }) const fetchData = async (query) => { const data = await fetch( `https://hn.algolia.com/api/v1/search?query=${query}` ).then(rsp => rsp.json()) state.hits = data.hits } onMounted(() => { watchEffect(() => { fetchData(state.query) }) }) const setQuery = () => { state.query = state.input } return { setQuery, state } } } </script>
可以注意到 button 綁定的 click 事件的方法,也是通過 setup() 方法返回的,我們可以將 setup() 方法返回值理解為 Vue2 中 data() 方法和 methods 對象的合并。
原先的返回值 state 變成了現(xiàn)在返回值的一個屬性,所以我們在模板層取數(shù)據(jù)的時候,需要進(jìn)行一些修改,在前面加上 state.。
<template> <input type="text" v-model="state.input" /> <button @click="setQuery">搜索</button> <ul> <li v-for="item of state.hits" :key="item.objectID" > <a :href="item.url">{{item.title}}</a> </li> </ul> </template>
返回數(shù)據(jù)修改
作為強(qiáng)迫癥患者,在模板層通過 state.xxx 的方式獲取數(shù)據(jù)實在是難受,那我們是不是可以通過對象解構(gòu)的方式將 state 的數(shù)據(jù)返回呢?
<template> <input type="text" v-model="input" /> <button class="search-btn" @click="setQuery">搜索</button> <ul class="results"> <li v-for="item of hits" :key="item.objectID" > <a :href="item.url">{{item.title}}</a> </li> </ul> </template> <script> import { reactive, onMounted, watchEffect } from 'vue' export default { setup(props, ctx) { const state = reactive({ input: 'vue', query: 'vue', hits: [] }) // 省略部分代碼... return { ...state, setQuery, } } } </script>
答案是『不可以』。修改代碼后,可以看到頁面雖然發(fā)起了請求,但是頁面并沒有展示數(shù)據(jù)。
state 在解構(gòu)后,數(shù)據(jù)就變成了靜態(tài)數(shù)據(jù),不能再被跟蹤,返回值類似于:
export default { setup(props, ctx) { // 省略部分代碼... return { input: 'vue', query: 'vue', hits: [], setQuery, } } }
為了跟蹤基礎(chǔ)類型的數(shù)據(jù)(即非對象數(shù)據(jù)),Vue3 也提出了解決方案:ref() 。
import { ref } from 'vue' const count = ref(0) console.log(count.value) // 0 count.value++ console.log(count.value) // 1
上面為 Vue 3 的官方案例,ref() 方法返回的是一個對象,無論是修改還是獲取,都需要取返回對象的 value 屬性。
我們將 state 從響應(yīng)對象改為一個普通對象,然后所有屬性都使用 ref 包裹,這樣修改后,后續(xù)的解構(gòu)才做才能生效。這樣的弊端就是,state 的每個屬性在修改時,都必須取其 value 屬性。但是在模板中不需要追加 .value,Vue 3 內(nèi)部有對其進(jìn)行處理。
import { ref, onMounted, watchEffect } from 'vue' export default { setup() { const state = { input: ref('vue'), query: ref('vue'), hits: ref([]) } const fetchData = async (query) => { const data = await fetch( `https://hn.algolia.com/api/v1/search?query=${query}` ).then(rsp => rsp.json()) state.hits.value = data.hits } onMounted(() => { watchEffect(() => { fetchData(state.query.value) }) }) const setQuery = () => { state.query.value = state.input.value } return { ...state, setQuery, } } }
有沒有辦法保持 state 為響應(yīng)對象,同時又支持其對象解構(gòu)的呢?當(dāng)然是有的,Vue 3 也提供了解決方案:toRefs() 。toRefs() 方法可以將一個響應(yīng)對象變?yōu)槠胀▽ο?,并且給每個屬性加上 ref()。
import { toRefs, reactive, onMounted, watchEffect } from 'vue' export default { setup() { const state = reactive({ input: 'vue', query: 'vue', hits: [] }) const fetchData = async (query) => { const data = await fetch( `https://hn.algolia.com/api/v1/search?query=${query}` ).then(rsp => rsp.json()) state.hits = data.hits } onMounted(() => { watchEffect(() => { fetchData(state.query) }) }) const setQuery = () => { state.query = state.input } return { ...toRefs(state), setQuery, } } }
Loading 與 Error 狀態(tài)
通常,我們發(fā)起請求的時候,需要為請求添加 Loading 和 Error 狀態(tài),我們只需要在 state 中添加兩個變量來控制這兩種狀態(tài)即可。
export default { setup() { const state = reactive({ input: 'vue', query: 'vue', hits: [], error: false, loading: false, }) const fetchData = async (query) => { state.error = false state.loading = true try { const data = await fetch( `https://hn.algolia.com/api/v1/search?query=${query}` ).then(rsp => rsp.json()) state.hits = data.hits } catch { state.error = true } state.loading = false } onMounted(() => { watchEffect(() => { fetchData(state.query) }) }) const setQuery = () => { state.query = state.input } return { ...toRefs(state), setQuery, } } }
同時在模板使用這兩個變量:
<template> <input type="text" v-model="input" /> <button @click="setQuery">搜索</button> <div v-if="loading">Loading ...</div> <div v-else-if="error">Something went wrong ...</div> <ul v-else> <li v-for="item of hits" :key="item.objectID" > <a :href="item.url">{{item.title}}</a> </li> </ul> </template>
展示 Loading、Error 狀態(tài):
將數(shù)據(jù)請求邏輯抽象
用過 umi 的同學(xué)肯定知道 umi 提供了一個叫做 useRequest 的 Hooks,用于請求數(shù)據(jù)非常的方便,那么我們通過 Vue 的組合 API 也可以抽象出一個類似于 useRequest 的公共方法。
接下來我們新建一個文件 useRequest.js :
import { toRefs, reactive, } from 'vue' export default (options) => { const { url } = options const state = reactive({ data: {}, error: false, loading: false, }) const run = async () => { state.error = false state.loading = true try { const result = await fetch(url).then(res => res.json()) state.data = result } catch(e) { state.error = true } state.loading = false } return { run, ...toRefs(state) } }
然后在 App.vue 中引入:
<template> <input type="text" v-model="query" /> <button @click="search">搜索</button> <div v-if="loading">Loading ...</div> <div v-else-if="error">Something went wrong ...</div> <ul v-else> <li v-for="item of data.hits" :key="item.objectID" > <a :href="item.url">{{item.title}}</a> </li> </ul> </template> <script> import { ref, onMounted } from 'vue' import useRequest from './useRequest' export default { setup() { const query = ref('vue') const { data, loading, error, run } = useRequest({ url: 'https://hn.algolia.com/api/v1/search' }) onMounted(() => { run() }) return { data, query, error, loading, search: run, } } } </script>
當(dāng)前的 useRequest 還有兩個缺陷:
傳入的 url 是固定的,query 修改后,不能及時的反應(yīng)到 url 上;
不能自動請求,需要手動調(diào)用一下 run 方法;
import { isRef, toRefs, reactive, onMounted, } from 'vue' export default (options) => { const { url, manual = false, params = {} } = options const state = reactive({ data: {}, error: false, loading: false, }) const run = async () => { // 拼接查詢參數(shù) let query = '' Object.keys(params).forEach(key => { const val = params[key] // 如果去 ref 對象,需要取 .value 屬性 const value = isRef(val) ? val.value : val query += `${key}=${value}&` }) state.error = false state.loading = true try { const result = await fetch(`${url}?${query}`) .then(res => res.json()) state.data = result } catch(e) { state.error = true } state.loading = false } onMounted(() => { // 第一次是否需要手動調(diào)用 !manual && run() }) return { run, ...toRefs(state) } }
經(jīng)過修改后,我們的邏輯就變得異常簡單了。
import useRequest from './useRequest' export default { setup() { const query = ref('vue') const { data, loading, error, run } = useRequest( { url: 'https://hn.algolia.com/api/v1/search', params: { query } } ) return { data, query, error, loading, search: run, } } }
感謝各位的閱讀,以上就是“Vue 3的組合API怎么請求數(shù)據(jù)”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對Vue 3的組合API怎么請求數(shù)據(jù)這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!
分享名稱:Vue3的組合API怎么請求數(shù)據(jù)
當(dāng)前網(wǎng)址:http://muchs.cn/article38/jpiisp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站維護(hù)、網(wǎng)站營銷、App設(shè)計、做網(wǎng)站、外貿(mào)建站、網(wǎng)站設(shè)計
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)