choukin

V1

2022/07/21阅读:49主题:默认主题

[Nuxt 3] (七)获取数据

获取

Nuxt 内置了 useFetch, useLazyFetch, useAsyncData, useLazyAsyncData 来处理应用程序的数据获取。

useFetch, useLazyFetch, useAsyncDatauseLazyAsyncData 只能在 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:trueuseFetch ,也就是这个异步函数不会阻塞导航,这意味着你需要处理数据为 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访问异步解析的数据。

你可能会疑惑:useFetchuseAsyncData的区别是什么? 简单的说,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:trueuseAsyncData . 换句话说,这个异步函数不会阻塞导航,着意味着你需要处理 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) 此外,它还具有方便的功能,例如自动解析响应和字符串化数据。

关注我的微信公众号查看更多
关注我的微信公众号查看更多

参考资料

[1]

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

分类:

前端

标签:

JavaScript

作者介绍

choukin
V1