GraphQL 入门: Apollo Client 存储API

GraphQL 入门: 简介
GraphQL 入门: Apollo Client - 简介
GraphQL 入门: Apollo Client - 安装和配置选项
GraphQL 入门: Apollo Client - 连接到数据
GraphQL 入门: Apollo Client - 网络层
GraphQL 入门: Apollo Client - 开发调试工具
GraphQL 入门: Apollo Client - 持久化GraphQL查询概要
GraphQL 入门: Apollo Client - 存储API
GraphQL 入门: Apollo Client - 查询(Batching)合并

Apollo Client 使用四个方法来控制存储:readQuery, readFragment, writeQuery, writeFragment. Apollo Client 的这些实例方法可以让你直接读写缓存.

用这四个方法能够让你自定义Apollo Client的行为. 下面我们介绍每个方法的细节.

从存储读取查询

第一个和存储交互的方法是readQuery. 该方法用于从根查询开始读取缓存数据, 下面是读取数据的例子:

const data = client.readQuery({
  query: gql`
  {
    todo(id: 1) {
      id
      text
      completed
    }
        
  }
  `
});

如果数据在缓存中已经存储, 它将会直接返回并存储到组件的data属性, 而不需要向服务器请求, 如果在缓存中找不到查询对应的数据, 将会抛出一个错误.

你可以在应用中任何地方使用一个查询, 还可以传入变量:

import { TodoQuery } form './TodoGraphQL';

const data = client.readQuery({
  query: TodoQuery,
  variables: {
    id: 5
  }
});

readQueryquery 类似, 但是readQuery 不会发送请求到服务器, 它总是期望从缓存中查询数据, 如果缓存中找不到对应的数据, 将会抛出一个错误.

从存储读取片段

有时候你希望重缓存中读取任意的数据, 而不仅仅是根查询类型. 对此有一个 readFragment() 方法用于这类用途. 该方法接受 GraphQL 片段和一个 ID, 并返回 ID 对应的数据片段.

client.readFragment({
  id: '5',
  fragment: gql`
    fragment todo on Todo {
      id
      text
      completed
    }
  `
});

id 应该是一个由 dataIdFromObject 函数返回的字符串, 这个字符串是在 Apollo Client 初始化的时候通过 dataIdFromObject 函数进行定义.

const client = new ApolloClient({
  dataIdFromObject: o => {
    if(o.__typename != null && o.id != null) {
      return `${o.__typename}-${o.id}`;
    }
  }
});

因为在id前面添加了变量__typename, 然后id的值变为Todo5.

向存储写入查询和片段

和读取查询和片段对应的还有 writeQuery()writeFragment() 方法. 这两个方法让你能够更新缓存中的数据, 用于模拟来自服务器的数据更新. 但是注意这些数据没有持久化到后端, 如果你刷新你的浏览器, 这些更新的数据将会丢失.

用户不会注意到有什么区别, 如果更新数据要对所有用户可见, 需要把更新的数据持久化到后端服务器.

writeQuerywriteFragment 的优点是, 它们让你能够修改缓存中的数据以确保数据能够同步到服务器, 并且在执行一次服务器完全刷新的时候不会丢失你的数据更新. 这让你能够部分的修改客户端数据, 给用户提供更好的体验.

writeQuery()readQuery 有相同的接口, 和 readQuery 不同的是 writeQuery 还有一个 data 参数. data 对象的结构必须和服务器返回的JSON结果的结构相同.

client.writeQuery({
  query: gql`
    {
      todo(id: 1) {
        completed
      }
    }
  `,
  data: {
    todo: {
      completed: true
    },
  },
});

同样的, writeFragment()readFragment() 也有相同的接口, 并且多一个 data 参数. id 遵循和 readFragment() 相同的规则:

client.writeFragment({
  id: '5',
  fragment: gql`
    fragment todo in Todo {
      completed
    }
  `,
  data: {
    completed: true
  }
});

这四个方法让你能够完全的控制缓存中的数据.

使用读和写来更新数据

因为从缓存中获取的数据只是一份拷贝, 不影响底层的存储.

const query = gql`
  {
    todos {
      id
      text
      completed
    }
  }
`;

const data = client.readQuery({
  query
});

data.todos.push({
  id: 5,
  text: 'Hello, world!',
  completed: false
});

client.writeQuery({
  query,
  data
});

在一个Mutation之后执行更新

const text = 'Hello, world!';
client.mutate({
  // GraphQL Mutation 更新语句
  mutation: gql`
    mutation ($text: String!) {
      createTodo(text: $text) {
        id
        text
        completed
      }
    }
  `,
  // 变量
  variables: {
    text,
  },
  optimisticResponse: {
    createTodo: {
      id: -1, // Fake id
      text,
      completed: false,
    },
  },
  // 更新函数
  // 用Mutation返回的结果对存储进行更新, 并触发React UI组件的重新渲染
  update: (proxy, mutationResult) => {
    const query = gql`
      {
        todos {
          id
          text
          completed
        }
      }
    `;
    // 从缓存中读取数据
    const data = proxy.readQuery({
      query,
    });
    // 用Mutation的结果更新数据
    data.todos.push(mutationResult.createTodo);
    // 写回缓存
    proxy.writeQuery({
      query,
      data,
    });
  },
});

update 函数有两个参数:

  • proxy 是一个 DataProxy 对象, 主要用于与底层存储进行数据交互

  • mutationResult 是一个Mutation操作的响应, 可以是一个乐观应答, 或服务器实际的应答.

updateQueries

也可以使用updateQueries回调函数对数据进行更新. 详细的API接口可参考 updateQueries

updateQueries 也是基于 Mutation 的返回结果对存储进行更新, 和 update 函数不同的是, 他会覆盖所有重叠的数据节点

参考资料

阅读 4.9k

推荐阅读