获取数据

在服务端获取数据

因为Nuxt3SSR的方案,所以你可能不仅仅只是想要在浏览器端发送请求获取数据,还想在服务器端就获取到数据并渲染组件。

Nuxt3提供了4种方式使得你可以在服务器端异步获取数据

  • useAsyncData
  • useLazyAsyncData (useAsyncData+lazy:true)
  • useFetch
  • useLazyFetch (useFetch+lazy:true)

注意:他们只能在setup或者是生命周期钩子中使用

useAsyncData

  • 项目中可以在pages目录、components目录和plugins目录下使用useAsyncData异步获取数据

    1. //用法
    2. const {
    3. data: Ref<DataT>,// 返回的数据结果
    4. pending: Ref<boolean>,// 是否在请求状态中
    5. refresh: (force?: boolean) => Promise<void>,// 强制刷新数据
    6. error?: any // 请求失败返回的错误信息
    7. } = useAsyncData(
    8. key: string, // 唯一键,确保相同的请求数据的获取和去重
    9. fn: () => Object,// 一个返回数值的异步函数
    10. options?: { lazy: boolean, server: boolean }
    11. // options.lazy,是否在加载路由后才请求该异步方法,默认为false
    12. // options.server,是否在服务端请求数据,默认为true
    13. // options.default,异步请求前设置数据data默认值的工厂函数(对lazy:true选项特别有用)
    14. // options.transform,更改fn返回结果的函数
    15. // options.pick,只从数组中指定的key进行缓存
    16. )

可以考虑设置lazy:true配合loading状态加载器的方式,能够带来更好的用户体验。

  1. // server/api/count.ts-例子
  2. let counter = 0
  3. export default () => {
  4. counter++
  5. return JSON.stringify(counter)
  6. }
  1. // app.vue-例子
  2. <script setup>
  3. const { data } = await useAsyncData('count', () => $fetch('/api/count'))
  4. </script>
  5. <template>
  6. Page visits: {{ data }}
  7. </template>

useLazyAsyncData

这个封装方法等同于是,使用useAsyncData方法默认配置了lazy:true,执行异步函数时不会阻塞路由的执行。也意味着你必须处理数据为null的情况(比如说,在default返回的工厂函数中设置一个默认值)。

useFetch

pages目录、components目录和plugins目录下使用useFetch也同样可以获取到任意的URL资源。该方法实际上是对useAsyncData和$fetch的封装,提供了一个更便捷的封装方法。(它会根据URL和fetch参数自动生成一个key,同时推断出API的响应类型)

  1. //useFetch用法
  2. const {
  3. data: Ref<DataT>,
  4. pending: Ref<boolean>,
  5. refresh: (force?: boolean) => Promise<void>,
  6. error?: any
  7. } = useFetch(url: string, options?)
  8. //可以看到useFetch和useAsyncData的返回对象是一样的,useFetch传参更便捷,不需要在fn中手动使用$fetch
  9. useAsyncData(key: string,fn: () => Object,options?: { lazy: boolean, server: boolean })
  1. //例子
  2. <script setup>
  3. const { data } = await useFetch('/api/count')
  4. </script>
  5. <template>
  6. Page visits: {{ data.count }}
  7. </template>

useLazyFetch

这个封装方法等同于是,使用useFetch方法默认配置了lazy:true,执行异步函数时不会阻塞路由的执行。也意味着你必须处理数据为null的情况(比如说,在default返回的工厂函数中设置一个默认值)。

最佳实践

只选取你需要使用的数据

通过异步请求回来的数据都会存储在页面payload中。意味着,可能会存在没有用在你的组件的数据也加载到了payload中。我们强烈推荐你只选取必须使用在组件上的数据

  1. // /api/mountains/everest
  2. {
  3. "title": "Mount Everest",
  4. "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",
  5. "height": "8,848 m",
  6. "countries": [
  7. "China",
  8. "Nepal"
  9. ],
  10. "continent": "Asia",
  11. "image": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f6/Everest_kalapatthar.jpg/600px-Everest_kalapatthar.jpg"
  12. }
  1. // 组件中只使用到了title和description,useFetch使用option的pick参数来指定key
  2. <script setup>
  3. const { data: mountain } = await useFetch('/api/mountains/everest', { pick: ['title', 'description'] })
  4. </script>
  5. <template>
  6. <h1>{{ mountain.title }}</h1>
  7. <p>{{ mountain.description }}</p>
  8. </template>

使用async setup

受Vue3的影响,如果你使用了async setup()的形式,当前组件的实例会在第一个异步操作之后丢失。如果你想要使用多个异步操作,比如执行多个useFetch,你需要使用<script setup>或者将多个请求封装在一起请求(使用Promise.all将多个请求装在一起)。 我们强烈建议使用 <script setup> 形式,它解决了采用顶层await的限制

  1. <script>
  2. export default defineComponent({
  3. async setup() {
  4. const [{ data: organization }, { data: repos }] = await Promise.all([
  5. useFetch(`https://api.github.com/orgs/nuxt`),
  6. useFetch(`https://api.github.com/orgs/nuxt/repos`)
  7. ])
  8. return {
  9. organization,
  10. repos
  11. }
  12. }
  13. })
  14. </script>
  15. <template>
  16. <header>
  17. <h1>{{ organization.login }}</h1>
  18. <p>{{ organization.description }}</p>
  19. </header>
  20. </template>