
choukin
2022/07/21阅读:49主题:默认主题
[Nuxt 3] (七)获取数据
获取
Nuxt 内置了 useFetch
, useLazyFetch
, useAsyncData
, useLazyAsyncData
来处理应用程序的数据获取。
useFetch
, useLazyFetch
, useAsyncData
和 useLazyAsyncData
只能在 setup
or Lifecycle Hooks
中使用
useFetch
在你的页面、组件和插件中,您可以使用 useFetch
请求任何URL,
这个组合是对 useAsyncData
和 $fetch
的封装,它根据URL和 请求的参数自动生成密钥,并推断API的返回类型。
::ReadMore{link="/api/composables/use-fetch"} ::
例子:
<script setup>
const { data: count } = await useFetch('/api/count')
</script>
<template>
Page visits: {{ count }}
</template>
:LinkExample{link="/examples/composables/use-fetch"}
useLazyFetch
这个组合等价于 设置了 lazy:true
的 useFetch
,也就是这个异步函数不会阻塞导航,这意味着你需要处理数据为 null
的情况,(或者在工厂函数中自定义默认值)。
::ReadMore{link="/api/composables/use-lazy-fetch"} ::
例子:
<template>
<!-- you'll need to handle a loading state -->
<div v-if="pending">
Loading ...
</div>
<div v-else>
<div v-for="post in posts">
<!-- do something -->
</div>
</div>
</template>
<script setup>
const { pending, data: posts } = useLazyFetch('/api/posts')
watch(posts, (newPosts) => {
// Because posts starts out null, you won't have access
// to its contents immediately, but you can watch it.
})
</script>
useAsyncData
在页面,组件和插件中,你可以通过 useAsyncData
访问异步解析的数据。
你可能会疑惑:useFetch
和 useAsyncData
的区别是什么? 简单的说,useFetch
接收一个URL作为参数来获取数据,而useSyncData
可能具有更多的逻辑。 useFetch(url)
等同于 useAsyncData(url,()=>$fetch(url))
- 通常情况下,有利于开发体验。
::ReadMore{link="/api/composables/use-async-data"} ::
例子
let counter = 0
export default () => {
counter++
return JSON.stringify(counter)
}
<script setup>
const { data } = await useAsyncData('count', () => $fetch('/api/count'))
</script>
<template>
Page visits: {{ data }}
</template>
:LinkExample{link="/examples/composables/use-async-data"}
useLazyAsyncData
这个组合函数等价于,设置了 lazy:true
的useAsyncData
. 换句话说,这个异步函数不会阻塞导航,着意味着你需要处理 data 为 null
的情况,(或者在工厂函数中自定义默认值)。
::ReadMore{link="/api/composables/use-lazy-async-data"} ::
例子:
<template>
<div>
{{ pending ? 'Loading' : count }}
</div>
</template>
<script setup>
const { pending, data: count } = useLazyAsyncData('count', () => $fetch('/api/count'))
watch(count, (newCount) => {
// Because count starts out null, you won't have access
// to its contents immediately, but you can watch it.
})
</script>
刷新数据
有时,用户在浏览页面的过程中,你可以能要刷新从API加载的数据,比如用户在选择分页,过滤结果,搜索等等情况。
你可以使用refresh()
方法 来刷新 useFetch()
中根据不同参数返回的数据。
<script setup>
const page = ref(1);
const { data: users, pending, refresh, error } = await useFetch(() => `users?page=${page.value}&take=6`, { baseURL: config.API_BASE_URL }
);
function previous(){
page.value--;
refresh();
}
function next() {
page.value++;
refresh();
}
</script>
这里的关键是,修改 useFetch()
使用的参数后调用refresh()
refreshNuxtData
想要让 useAsyncData
useLazyAsyncData
useFetch
useLazyFetch
的缓冲失效,并触发重新请求。
当你想要刷新当前页面所有的数据时,可以使用这个方法。
::ReadMore{link="/api/utils/refresh-nuxt-data"} ::
例如:
<template>
<div>
{{ pending ? 'Loading' : count }}
</div>
<button @click="refresh">Refresh</button>
</template>
<script setup>
const { pending, data: count } = useLazyAsyncData('count', () => $fetch('/api/count'))
const refresh = () => refreshNuxtData('count')
</script>
同构 fetch
和 $fetch
当我们在浏览器中调用 fetch
时,像cookie
这样的用户header 信息会直接发送给API,但是当服务端渲染时,,由于fetch
请求时在服务端内部,因此它不包括用户浏览器端的cookies,也不从fetch响应传递cookie。
::ReadMore{link="/api/utils/$fetch"} ::
例子: 给API发送header 信息
我们可以使用 useRequestHeaders
从服务端访问并代理cookie给API
下面的例子将请求header 信息添加到同构的$fetch
调用中,来确保API端可以访问由用户端发送来的 cookie
.
<script setup>
const { data } = await useFetch('/api/me', {
headers: useRequestHeaders(['cookie'])
})
</script>
在将header代理到外部APi前要小心使用,只包含你需要的头部信息, 不是所有的header信息都可以安全传递,也有可能引起不确定的问题。
下面列出了常见的不需要代理的header信息。
-
host
,accept
-
content-length
,content-md5
,content-type
-
x-forwarded-host
,x-forwarded-port
,x-forwarded-proto
-
cf-connecting-ip
,cf-ray
例子: 在服务端API调用时,传递Cookie
如果你想要从内部请求向客户端传递或者代理 cookie,你需要自己处理这种情况
export const fetchWithCookie = async (url: string, cookieName: string) => {
const response = await $fetch.raw(url)
if (process.server) {
const cookies = Object.fromEntries(
response.headers.get('set-cookie')?.split(',').map((a) => a.split('='))
)
if (cookieName in cookies) {
useCookie(cookieName).value = cookies[cookieName]
}
}
return response._data
}
<script setup lang="ts">
// This composable will automatically pass on a cookie of our choice.
const result = await fetchWithCookie("/api/with-cookie", "test")
onMounted(() => console.log(document.cookie))
</script>
最佳实践
这些组合项返回的数据会被存储在页面负载内,这意味着返回的每个未在组件中使用的key 都会被添加到负载中。
我们强烈建议你,只选择组件中用到的key.
加入 /api/mountains/everest
返回下面的对象:
{
"title": "Mount Everest",
"description": "Mount Everest is Earth's highest mountain above sea level, located in the Mahalangur Himal sub-range of the Himalayas. The China–Nepal border runs across its summit point",
"height": "8,848 m",
"countries": [
"China",
"Nepal"
],
"continent": "Asia",
"image": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f6/Everest_kalapatthar.jpg/600px-Everest_kalapatthar.jpg"
}
如果你只打算在你的组件中使用title
description
你可以通过$fetch
或者 pick
链式调用 选出你想要的key:
<script setup>
const { data: mountain } = await useFetch('/api/mountains/everest', { pick: ['title', 'description'] })
</script>
<template>
<h1>{{ mountain.title }}</h1>
<p>{{ mountain.description }}</p>
</template>
使用异步 setup
如果你想使用async setup()
,当前的组件实力会在第一个await
后消失,(这是Vue3的限制)如果你想使用多个异步操作,比如多次调用 useFetch
,你会需要使用 <script setup>
或者在setup末尾等待它们一起执行。
推荐使用<script setup>
它避免了顶层使用await的限制,阅读更多[1].
<script>
export default defineComponent({
async setup() {
const [{ data: organization }, { data: repos }] = await Promise.all([
useFetch(`https://api.github.com/orgs/nuxt`),
useFetch(`https://api.github.com/orgs/nuxt/repos`)
])
return {
organization,
repos
}
}
})
</script>
<template>
<header>
<h1>{{ organization.login }}</h1>
<p>{{ organization.description }}</p>
</header>
</template>
直接调用API
在某些情况下,你可能需要直接调用API,Nuxt3提供了一个全局可用的$fetch
方法,该方法使用unjs/ohmyfetch[2](除fetch
外),它的API和原生fetch[3]一样。
$fetch
有如下优点: 在服务器端它会智能得处理直接调用API,如果运行在客户端,它会对你的API进行调用,(它还可以处理调用第三方API) 此外,它还具有方便的功能,例如自动解析响应和字符串化数据。
参考资料
Vue setup: https://vuejs.org/api/sfc-script-setup
[2]ohmyfetch: https://github.com/unjs/ohmyfetch
[3]fetch: https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch
作者介绍
