You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Apollo Client policies中使用readField对带参数的查询做子选择

Apollo Client 3.4.7 带参数查询的缓存子集读取最优方案

核心实现思路

Apollo 官方的 readField 方法原生支持传入变量参数,会自动按照缓存规则拼接带参数的缓存键,不需要手动拼接键名,也不需要直接调用 cache.readQuery 处理引用转换。

具体实现步骤

  1. 先给自定义的 readManagers 字段声明和原查询一致的入参,保证参数可以透传到读取逻辑
  2. 在 read 函数中调用 readField 时传入对应变量,Apollo 会自动匹配对应参数的缓存数据

完整代码示例

import { TypePoliciesSchema, gql, readField } from '@apollo/client';

const schema: TypePoliciesSchema = {
  typeDefs: gql`
    extend type Query {
      // 声明和原employees查询一致的入参
      readManagers(companyId: ID!): [Employee!]!
    }
  `,
  typePolicies: {
    Query: {
      fields: {
        readManagers: {
          read(_, context) {
            // 直接传入变量,readField自动匹配对应缓存
            const employees = readField<Query, 'readEmployees'>(
              context, 
              'readEmployees',
              undefined,
              // 第四个参数传入变量,Apollo自动生成对应缓存键
              { companyId: context.args.companyId }
            );

            return employees?.filter(employeeRef => {
              const employeeRole = readField<Employee, 'role'>(context, 'role', employeeRef);
              return employeeRole === EmployeeRole.Manager;
            }) ?? [];
          },
        }
      }
    }
  }
}

如果用 context.readField 写法,代码更简洁:

read(_, context) {
  const employees = context.readField(
    'readEmployees',
    undefined,
    { companyId: context.args.companyId }
  );
  // 后续过滤逻辑不变
}

进一步减少样板代码的封装方案

如果有大量类似的子集读取需求,可以封装通用的字段生成函数,避免重复写逻辑:

import { FieldFunctionOptions } from '@apollo/client';

type FilterFieldConfig<T = unknown> = {
  // 原查询字段名
  sourceField: string;
  // 过滤规则
  filter: (item: T, context: FieldFunctionOptions) => boolean;
}

function createFilteredField<T = unknown>(config: FilterFieldConfig<T>) {
  return {
    read(_: unknown, context: FieldFunctionOptions) {
      const sourceList = context.readField(
        config.sourceField,
        undefined,
        context.args
      ) as T[];
      return sourceList?.filter(item => config.filter(item, context)) ?? [];
    }
  }
}

封装后使用方式如下:

const schema: TypePoliciesSchema = {
  typeDefs: gql`
    extend type Query {
      readManagers(companyId: ID!): [Employee!]!
      readDesigners(companyId: ID!): [Employee!]!
    }
  `,
  typePolicies: {
    Query: {
      fields: {
        readManagers: createFilteredField<Employee>({
          sourceField: 'readEmployees',
          filter: (emp, ctx) => ctx.readField('role', emp) === EmployeeRole.Manager
        }),
        readDesigners: createFilteredField<Employee>({
          sourceField: 'readEmployees',
          filter: (emp, ctx) => ctx.readField('role', emp) === EmployeeRole.Designer
        }),
      }
    }
  }
}

方案优势

  • 完全复用 Apollo 原生缓存逻辑,不需要手动处理缓存键拼接,不存在匹配错误问题
  • 读取到的都是缓存原生引用,响应式更新和原查询完全一致,没有性能损耗
  • 封装后几乎没有样板代码,扩展新的子集查询成本极低

内容的提问来源于stack exchange,提问作者R. Chappell

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.08 18:06:02