Apollo Client 缓存处理小结 中提到了书架功能存在的问题:
每一本书籍对应一个查询(即一个 HTTP 请求),当从未登录状态切换到登录状态时,主页所有已经加载的书籍都会发送请求,判断它是否在当前用户的书架上。
理想的实现中,在用户登录后,应该只发送一个请求,用于获取书单信息流。
改进
后端
- 将查询
isBookInBookshelf
移动到 Book
类型下
1 2 3 4 5 6
| const bookType = ` type Book { ... isBookInBookshelf(userId: ID!): Boolean! } `;
|
- 实现该字段的
resolver
函数
1 2 3 4 5 6 7 8 9 10 11
| const bookResolver = { ... Book: { ... isBookInBookshelf: async({ id }, { userId } , { models }) => { if(userId === "") return false; const user = await models.User.findById(userId); return user.bookShelf.indexOf(id) !== -1; } } };
|
前端
- 修改
GET_CATEGORY_FEED schema
,接收 userId
参数,返回的书籍中包含 isBookInBookshelf
字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| export const GET_CATEGORY_FEED = gql` query getCategoryFeed( ... $userId: ID! ) { categoryFeed(first: $first, after: $after) { pageInfo { hasNextPage endCursor } edges { node { id name items(first: $itemsFirst, after: $itemsAfter) { pageInfo { endCursor hasNextPage } edges { node { id ... isBookInBookshelf(userId: $userId) } } } } } } } `;
|
- 在组件中修改查询,传入参数
userId
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const userId = useLoggedInUserId(); const { result: categoryFeedResult, loading: categoryFeedLoading, error: categoryFeedError, fetchMore, networkStatus, } = useQuery( GET_CATEGORY_FEED, () => ({ after: after.value, first: first.value, itemsAfter: "", itemsFirst: 3, userId }), { notifyOnNetworkStatusChange: true, } );
|
- 修改缓存的处理方式
1 2 3 4 5 6 7 8 9 10 11 12
| const cache = new InMemoryCache({ typePolicies: { ... Book: { fields: { isBookInBookshelf: { keyArgs: ["userId"] } } } }, });
|
- 修改添加书籍或删除书籍时的逻辑
乐观更新:先修改客户端状态,再发送网络请求。如果请求失败,重置客户端状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| const addToBookShelf = () => { isInBookshelf.value = !isInBookshelf.value; toggleBookshelf(); };
const { mutate: toggleBookshelf, onDone: onToggle } = useMutation( TOGGLE_BOOKSHELF_MUTATION, () => ({ variables: { bookId: bookId.value, userId, }, }) );
onToggle(({ data: { toggleBookshelf } }) => { if (toggleBookshelf.success === true) { toast.success(toggleBookshelf.message); } else { isInBookshelf.value = !isInBookshelf.value; toast.info(toggleBookshelf.message); } });
|