Files
2026-07-13 21:36:20 +08:00

7.5 KiB
Raw Permalink Blame History

查询模式

本参考文档涵盖了编写高效 GraphQL 查询的模式。

目录

查询结构

基本查询

query GetUser($id: ID!) {
  user(id: $id) {
    id
    name
    email
  }
}

组成部分:

  • query — 操作类型
  • GetUser — 操作名称
  • ($id: ID!) — 变量定义
  • user(id: $id) — 带参数的字段
  • { id name email } — 选择集

多根字段查询

query GetDashboardData($userId: ID!) {
  user(id: $userId) {
    id
    name
  }
  notifications(first: 5) {
    id
    message
  }
  stats {
    totalPosts
    totalComments
  }
}

嵌套查询

query GetUserWithPosts($userId: ID!) {
  user(id: $userId) {
    id
    name
    posts(first: 10) {
      edges {
        node {
          id
          title
          comments(first: 3) {
            edges {
              node {
                id
                body
              }
            }
          }
        }
      }
    }
  }
}

字段选择

只请求需要的字段

# 用户卡片组件
query GetUserCard($id: ID!) {
  user(id: $id) {
    id
    name
    avatarUrl
    # 如果不展示,不要请求 email、bio 等字段
  }
}

始终包含 ID 字段

为可能缓存或重新获取的类型包含 id

query GetPost($id: ID!) {
  post(id: $id) {
    id # 始终包含,用于缓存
    title
    author {
      id # 包含,用于作者缓存条目
      name
    }
  }
}

选择连接(Connection

对于分页数据,按需请求:

query GetUserPosts($userId: ID!, $first: Int!, $after: String) {
  user(id: $userId) {
    id
    posts(first: $first, after: $after) {
      edges {
        node {
          id
          title
          excerpt
        }
        cursor # 仅在实现无限滚动时需要
      }
      pageInfo {
        hasNextPage
        endCursor
      }
      totalCount # 仅在展示总数时需要
    }
  }
}

别名

基本别名

在响应中重命名字段:

query GetUserNames($id: ID!) {
  user(id: $id) {
    userId: id
    displayName: name
  }
}

# 响应:{ user: { userId: "123", displayName: "John" } }

多次查询同一字段

query GetMultipleUsers {
  admin: user(id: "1") {
    id
    name
  }
  moderator: user(id: "2") {
    id
    name
  }
  currentUser: user(id: "3") {
    id
    name
  }
}

使用不同参数的别名

query GetPostsByStatus($userId: ID!) {
  user(id: $userId) {
    id
    publishedPosts: posts(status: PUBLISHED, first: 5) {
      edges {
        node {
          id
          title
        }
      }
    }
    draftPosts: posts(status: DRAFT, first: 5) {
      edges {
        node {
          id
          title
        }
      }
    }
  }
}

指令

@include 指令

仅当条件为 true 时包含字段:

query GetUser($id: ID!, $includeEmail: Boolean!) {
  user(id: $id) {
    id
    name
    email @include(if: $includeEmail)
  }
}

# 变量:{ id: "123", includeEmail: true }
# 返回 email 字段

# 变量:{ id: "123", includeEmail: false }
# 不返回 email 字段

@skip 指令

当条件为 true 时跳过字段:

query GetPost($id: ID!, $isPreview: Boolean!) {
  post(id: $id) {
    id
    title
    content @skip(if: $isPreview)
    excerpt
  }
}

在片段上使用指令

query GetUser($id: ID!, $expanded: Boolean!) {
  user(id: $id) {
    id
    name
    ...UserDetails @include(if: $expanded)
  }
}

fragment UserDetails on User {
  bio
  website
  socialLinks {
    platform
    url
  }
}

组合使用指令

query GetPost($id: ID!, $showComments: Boolean!, $hideAuthor: Boolean!) {
  post(id: $id) {
    id
    title
    author @skip(if: $hideAuthor) {
      id
      name
    }
    comments(first: 10) @include(if: $showComments) {
      edges {
        node {
          id
          body
        }
      }
    }
  }
}

查询命名

命名约定

用途 模式 示例
获取单个条目 Get{Type} GetUserGetPost
获取列表 List{Types} ListUsersListPosts
搜索 Search{Types} SearchUsersSearchProducts
为特定 UI 获取 Get{Feature}Data GetDashboardDataGetProfilePage

好的命名

query GetUserProfile($id: ID!) { ... }
query ListRecentPosts($first: Int!) { ... }
query SearchProducts($query: String!) { ... }
query GetOrderDetails($orderId: ID!) { ... }
query GetHomeFeed($userId: ID!) { ... }

避免泛化命名

# 避免
query Data { ... }
query Query1 { ... }
query FetchStuff { ... }

# 推荐
query GetCurrentUser { ... }
query ListActiveProjects { ... }
query SearchCustomers($query: String!) { ... }

查询组织

每个文件一个查询

src/
  graphql/
    queries/
      GetUser.graphql
      ListPosts.graphql
      SearchProducts.graphql
# GetUser.graphql
query GetUser($id: ID!) {
  user(id: $id) {
    id
    name
    email
  }
}

与组件同目录放置

src/
  components/
    UserProfile/
      UserProfile.tsx
      UserProfile.graphql
      UserProfile.test.tsx

导入并使用

// 使用 graphql-tag
import { gql } from "@apollo/client";

export const GET_USER = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
    }
  }
`;

// 使用 .graphql 文件(需要加载器)
import { GetUserDocument } from "./UserProfile.generated";

性能优化

避免过度获取

只请求组件会用到的字段:

# 列表视图——最少字段
query ListPostsForIndex {
  posts(first: 20) {
    edges {
      node {
        id
        title
        excerpt
        author { name }
      }
    }
  }
}

# 详情视图——更多字段
query GetPostDetail($id: ID!) {
  post(id: $id) {
    id
    title
    content
    publishedAt
    author {
      id
      name
      bio
      avatarUrl
    }
    comments(first: 10) { ... }
  }
}

使用分页

切勿获取无界列表:

# 避免
query GetAllPosts {
  posts {
    # 可能返回数千条
    id
    title
  }
}

# 推荐
query GetPosts($first: Int = 20, $after: String) {
  posts(first: $first, after: $after) {
    edges {
      node {
        id
        title
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

批量合并相关查询

在一次请求中获取相关数据:

# 替代多次查询
query GetDashboard($userId: ID!) {
  user(id: $userId) {
    id
    name
  }
  recentPosts: posts(first: 5, orderBy: { field: CREATED_AT, direction: DESC }) {
    edges {
      node {
        id
        title
      }
    }
  }
  notifications(first: 10, unreadOnly: true) {
    edges {
      node {
        id
        message
      }
    }
  }
}

对重复选择集使用片段

query GetPostsWithAuthors {
  posts(first: 10) {
    edges {
      node {
        id
        title
        author {
          ...AuthorInfo
        }
      }
    }
  }
  featuredPost {
    id
    title
    author {
      ...AuthorInfo
    }
  }
}

fragment AuthorInfo on User {
  id
  name
  avatarUrl
}