跳至内容

查询

获取数据涉及使用标准 GraphQL 文档执行 **查询** 操作。您可以了解更多关于查询和 GraphQL 文档的信息 这里在游乐场练习运行查询

执行查询

GraphQL 文档

让我们在本节中以这个示例 GraphQL 文档为例

graphql
query getUsers {
  users {
    id
    firstname
    lastname
    email
  }
}

提示

建议为您的 GraphQL 操作命名(这里为 getUsers),这样在 Apollo Client 开发工具中更容易找到它们。

此查询将返回一个 data 对象,其中包含一个 users 数组,其中包含它们的 idfirstnamelastnameemail。它可能看起来像这样

json
{
  "data": {
    "users": [
      {
        "id": "abc",
        "firstname": "James",
        "lastname": "Holden",
        "email": "james.holden@roci.com"
      },
      {
        "id": "def",
        "firstname": "Naomi",
        "lastname": "Nagata",
        "email": "naomi.nagata@roci.com"
      }
    ]
  }
}

您可能会问:为什么 data 上有一个嵌套的 users 属性?为什么数组不直接在 data 上?

这是因为您可以在 GraphQL 操作中选择多个根字段

graphql
query getCatsAndDogs {
  cats {
    id
  }

  dogs {
    id
  }
}

在这种情况下,结果可能看起来像这样

json
{
  "data": {
    "cats": [
      { "id": "abc" },
      { "id": "def" }
    ],
    "dogs": [
      { "id": "ghi" },
      { "id": "jkl" }
    ]
  }
}

结果中还可能存在除 data 之外的其他可选属性

  • errors:服务器返回的错误数组
  • extensions:其他信息,例如执行时间

useQuery

用于执行查询的主要组合函数是 useQuery。在您的组件中,首先导入它

vue
<script>
import { useQuery } from '@vue/apollo-composable'

export default {
  setup () {
    // Your data & logic here...
  },
}
</script>

您可以在 setup 选项中使用 useQuery,并将 GraphQL 文档作为第一个参数传递给它。然后检索查询 result

vue
<script>
import { useQuery } from '@vue/apollo-composable'
import gql from 'graphql-tag'

export default {
  setup () {
    const { result } = useQuery(gql`
      query getUsers {
        users {
          id
          firstname
          lastname
          email
        }
      }
    `)
  },
}
</script>

请注意,这里的 result 是一个 Ref,它保存了 Apollo 返回的结果中的数据。

如果您想直接访问数据对象,请使用 result.value

vue
<script>
import { watch } from 'vue'
import { useQuery } from '@vue/apollo-composable'
import gql from 'graphql-tag'

export default {
  setup () {
    const { result } = useQuery(gql`
      query getUsers {
        users {
          id
          firstname
          lastname
          email
        }
      }
    `)

    watch(() => {
      console.log(result.value)
    })
  },
}
</script>

在这个例子中,您也可以直接观察 Ref

vue
<script>
import { watch } from 'vue'
import { useQuery } from '@vue/apollo-composable'
import gql from 'graphql-tag'

export default {
  setup () {
    const { result } = useQuery(gql`
      query getUsers {
        users {
          id
          firstname
          lastname
          email
        }
      }
    `)

    watch(result, value => {
      console.log(value)
    })
  },
}
</script>

让我们在模板中公开我们的结果

vue
<script>
import { useQuery } from '@vue/apollo-composable'
import gql from 'graphql-tag'

export default {
  setup () {
    const { result } = useQuery(gql`
      query getUsers {
        users {
          id
          firstname
          lastname
          email
        }
      }
    `)

    return {
      result,
    }
  },
}
</script>

<template>
  <ul>
    <li v-for="user of result.users" :key="user.id">
      {{ user.firstname }} {{ user.lastname }}
    </li>
  </ul>
</template>

请注意,result 可能并不总是包含您的数据!它最初将是 undefined,直到查询成功完成。因此,在渲染数据之前添加一个条件是一个好主意

vue
<template>
  <ul v-if="result && result.users">
    <li v-for="user of result.users" :key="user.id">
      {{ user.firstname }} {{ user.lastname }}
    </li>
  </ul>
</template>

我们可以使用一个 computed 属性来简化对结果部分的访问,并提供一个默认值

vue
<script>
import { computed } from 'vue'
import { useQuery } from '@vue/apollo-composable'
import gql from 'graphql-tag'

export default {
  setup () {
    const { result } = useQuery(gql`
      query getUsers {
        users {
          id
          firstname
          lastname
          email
        }
      }
    `)

    const users = computed(() => result.value?.users ?? [])

    return {
      users,
    }
  },
}
</script>

<template>
  <ul>
    <li v-for="user of users" :key="user.id">
      {{ user.firstname }} {{ user.lastname }}
    </li>
  </ul>
</template>

查询状态

加载状态

除了 result 之外,useQuery 还返回 loading,这是一个布尔 Ref,用于跟踪查询的加载状态

vue
<script>
import { useQuery } from '@vue/apollo-composable'
import gql from 'graphql-tag'

export default {
  setup () {
    const { result, loading } = useQuery(gql`
      query getUsers {
        users {
          id
          firstname
          lastname
          email
        }
      }
    `)

    return {
      result,
      loading,
    }
  },
}
</script>

<template>
  <div v-if="loading">Loading...</div>

  <ul v-else-if="result && result.users">
    <li v-for="user of result.users" :key="user.id">
      {{ user.firstname }} {{ user.lastname }}
    </li>
  </ul>
</template>

错误

还有一个 error Ref,它保存了请求过程中可能发生的任何错误

vue
<script>
import { useQuery } from '@vue/apollo-composable'
import gql from 'graphql-tag'

export default {
  setup () {
    const { result, loading, error } = useQuery(gql`
      query getUsers {
        users {
          id
          firstname
          lastname
          email
        }
      }
    `)

    return {
      result,
      loading,
      error,
    }
  },
}
</script>

<template>
  <div v-if="loading">Loading...</div>

  <div v-else-if="error">Error: {{ error.message }}</div>

  <ul v-else-if="result && result.users">
    <li v-for="user of result.users" :key="user.id">
      {{ user.firstname }} {{ user.lastname }}
    </li>
  </ul>
</template>

变量

您可以将 variables 对象传递给 useQuery 的第二个参数

js
const { result } = useQuery(gql`
  query getUserById ($id: ID!) {
    user (id: $id) {
      id
      email
    }
  }
`, {
  id: 'abc-abc-abc',
})

变量 Ref

您可以通过检索它们的 variables Ref 来稍后更改它们

js
const { result, variables } = useQuery(gql`
  query getUserById ($id: ID!) {
    user (id: $id) {
      id
      email
    }
  }
`, {
  id: 'abc-abc-abc',
})

function selectUser (id) {
  variables.value = {
    id,
  }
}

提示

这将每次 variables 对象中的属性更改时重新获取查询。

或者,您可以直接传递一个 Ref

js
import { ref } from 'vue'
js
const variables = ref({
  id: 'abc-abc-abc',
})

const { result } = useQuery(gql`
  query getUserById ($id: ID!) {
    user (id: $id) {
      id
      email
    }
  }
`, variables)

function selectUser (id) {
  variables.value = {
    id,
  }
}

响应式对象

您还可以传递一个响应式对象

js
import { reactive } from 'vue'
js
const variables = reactive({
  id: 'abc-abc-abc',
})

const { result } = useQuery(gql`
  query getUserById ($id: ID!) {
    user (id: $id) {
      id
      email
    }
  }
`, variables)

function selectUser (id) {
  variables.id = id
}

这也意味着您可以直接传递 setup 中的 props,因为 props 已经是响应式对象

js
export default {
  props: ['id'],

  setup (props) {
    const { result } = useQuery(gql`
      query getUserById ($id: ID!) {
        user (id: $id) {
          id
          email
        }
      }
    `, props)

    return {
      result,
    }
  },
}

但请注意,如果您添加了 GraphQL 文档中未使用的新的 props,您将遇到 GraphQL 验证错误!

变量函数

最后,您可以将变量作为返回对象的函数传递

js
export default {
  props: ['id'],

  setup (props) {
    const { result } = useQuery(gql`
      query getUserById ($id: ID!) {
        user (id: $id) {
          id
          email
        }
      }
    `, () => ({
      id: props.id,
    }))

    return {
      result,
    }
  },
}

此变量函数将自动变为响应式,因此每当 props.id 更改时,查询的 variables 对象都会更新。

此语法在您想在 variables 中使用一些 Ref 时也很有用

js
const id = ref('abc-abc-abc')

const { result } = useQuery(gql`
  query getUserById ($id: ID!) {
    user (id: $id) {
      id
      email
    }
  }
`, () => ({
  id: id.value
}))

function selectUser (id) {
  id.value = id
}

选项

useQuery 的第三个参数是一个选项对象,用于配置您的查询。

variables 一样,您可以传递一个 Ref、一个响应式对象或一个将自动变为响应式的函数。

使用 Ref

js
const options = ref({
  fetchPolicy: 'cache-first',
})

const { result } = useQuery(gql`
  query getUsers {
    users {
      id
      email
    }
  }
`, null, options)

使用响应式对象

js
const options = reactive({
  fetchPolicy: 'cache-first',
})

const { result } = useQuery(gql`
  query getUsers {
    users {
      id
      email
    }
  }
`, null, options)

使用将自动变为响应式的函数

js
const fetchPolicy = ref('cache-first')

const { result } = useQuery(gql`
  query getUsers {
    users {
      id
      email
    }
  }
`, null, () => ({
  fetchPolicy: fetchPolicy.value
}))

有关所有可能的选项,请参阅 API 参考

禁用查询

您可以使用 enabled 选项禁用和重新启用查询

js
const enabled = ref(false)

const { result } = useQuery(gql`
  ...
`, null, () => ({
  enabled: enabled.value,
}))

function enableQuery () {
  enabled.value = true
}

获取策略

fetchPolicy 选项允许您自定义查询将如何使用 Apollo Client 缓存。

js
const { result } = useQuery(gql`
  ...
`, null, {
  fetchPolicy: 'cache-and-network',
})

可用值是

  • cache-first(默认):从缓存中返回结果。仅当缓存结果不可用时才从网络获取。
  • cache-and-network:首先从缓存中返回结果(如果存在),然后在网络结果可用时返回网络结果。
  • cache-only:如果可用,则从缓存中返回结果,否则失败。
  • network-only:从网络返回结果,如果网络调用不成功则失败,保存到缓存。
  • no-cache:从网络返回结果,如果网络调用不成功则失败,不保存到缓存。

更新缓存结果

查询完成后,它将使用结果数据更新缓存(取决于 获取策略)。这提高了下次在应用程序中需要渲染数据时的性能,并确保依赖于数据的所有组件始终保持一致。

但是,您有时希望确保此数据与服务器相比是最新的。

轮询

轮询意味着反复调用服务器以自动更新查询数据。

您可以使用 pollInterval 启用轮询,它将是每次重复向服务器发出的请求之间的间隔(以毫秒为单位)。

在这个例子中,我们将每秒轮询服务器一次

js
const { result } = useQuery(gql`
  ...
`, null, {
  pollInterval: 1000,
})

重新获取

另一种方法是在响应事件时手动再次执行查询,而不是使用固定间隔。

这是使用 refetch 函数完成的

vue
<script>
import { useQuery } from '@vue/apollo-composable'
import gql from 'graphql-tag'

export default {
  setup () {
    const { result, loading, error, refetch } = useQuery(gql`
      query getUsers {
        users {
          id
          firstname
          lastname
          email
        }
      }
    `)

    const users = computed(() => result.value?.users)

    return {
      users,
      loading,
      error,
      refetch,
    }
  },
}
</script>

<template>
  <div v-if="loading">Loading...</div>

  <div v-else-if="error">Error: {{ error.message }}</div>

  <ul v-else-if="users">
    <li v-for="user of users" :key="user.id">
      {{ user.firstname }} {{ user.lastname }}
    </li>

    <button @click="refetch()">Refresh</button>
  </ul>
</template>

事件钩子

useQuery 返回事件钩子,允许您在发生特定事件时执行代码。

onResult

每当有新结果可用时,就会调用它。

js
const { onResult } = useQuery(...)

onResult(queryResult => {
  console.log(queryResult.data)
  console.log(queryResult.loading)
  console.log(queryResult.networkStatus)
  console.log(queryResult.stale)
})

您可以传递 notifyOnNetworkStatusChange 选项以强制查询在网络状态或错误更新时触发新结果

js
useQuery(gql`
  ...
`, null, {
  notifyOnNetworkStatusChange: true,
})

onError

它在发生错误时触发

js
const { onError } = useQuery(...)

onError(error => {
  console.log(error.graphQLErrors)
  console.log(error.networkError)
})

您可以使用 @vue/apollo-util 包中的 logErrorMessages 函数来格式化浏览器控制台中的错误

js
import { logErrorMessages } from '@vue/apollo-util'

const { onError } = useQuery(...)

onError(error => {
  logErrorMessages(error)
})

示例错误

Error log screenshot

如果您使用的是 Webpack 或 Vue CLI,最好只在开发中使用它

js
import { logErrorMessages } from '@vue/apollo-util'

const { onError } = useQuery(...)

onError(error => {
  if (process.env.NODE_ENV !== 'production') {
    logErrorMessages(error)
  }
})

这样,在为生产编译项目时,它将被删除。

延迟查询

如果您需要等待才能开始查询,可以使用 useLazyQuery 而不是 useQuery。它返回一个额外的 load 函数来启动查询您的 API。

示例

vue
<script>
import { computed } from 'vue'
import gql from 'graphql-tag'
import { useLazyQuery } from '@vue/apollo-composable'

export default {
  setup () {
    const { result, load } = useLazyQuery(gql`
      query list {
        list
      }
    `)
    const list = computed(() => result.value?.list ?? [])

    return {
      load,
      list,
    }
  },
}
</script>

<template>
  <div class="m-6">
    <button
      class="bg-green-200 rounded-lg p-4"
      @click="load()"
    >
      Load list
    </button>

    <ul class="my-4">
      <li
        v-for="(item, index) of list"
        :key="index"
        class="list-disc ml-6"
      >
        {{ item }}
      </li>
    </ul>
  </div>
</template>

获取结果

load() 返回一个 Promise,用于获取第一次请求的结果(如果这是第一次激活查询)。

js
const { result, load, refetch } = useLazyQuery(gql`
  query list {
    list
  }
`)
// ...

async function myLoad () {
  try {
    const result = await load()
  } catch (e) {
    // Handle error
  }
}

重新获取延迟查询

如果这不是第一次激活查询,load() 将返回 false。您可以使用它来使用 refetch() 重新获取查询,以防用户再次单击按钮,这意味着 load() 返回 false

js
const { result, load, refetch } = useLazyQuery(gql`
  query list {
    list
  }
`)
// ...

function loadOrRefetch () {
  load() || refetch()
}