onBlur触发GraphQL请求报错Apollo已创建,如何实现多次请求?
解决Apollo客户端重复创建导致的
Apollo has been already created错误 嘿,这个问题其实很常见——你每次触发onBlur事件时都在调用this._apollo.create()重新创建Apollo客户端实例,但Apollo客户端是全局单例模式,只能被初始化一次!第二次调用create()自然就会抛出那个错误啦。
我们只需要把Apollo客户端的初始化逻辑移到组件的初始化阶段,而不是每次onBlur都重复创建。然后在onBlurZIP里只负责发起查询请求就好,具体修改如下:
修改后的组件代码
import { Component, OnInit, OnDestroy } from '@angular/core'; import { Apollo, gql } from 'apollo-angular'; import { createHttpLink } from 'apollo-link-http'; import { setContext } from 'apollo-link-context'; import { InMemoryCache } from '@apollo/client/core'; import { Subscription } from 'rxjs'; @Component({ // 你的组件元数据(selector、templateUrl等) }) export class YourComponent implements OnInit, OnDestroy { querySubscription: Subscription | undefined; constructor(private _apollo: Apollo) {} ngOnInit(): void { // 仅在组件初始化时创建一次Apollo客户端 const httpLink = createHttpLink({ uri: environment.endpoint }); const authLink = setContext((_, { headers }) => { // 这里保留你的headers逻辑,比如添加认证token等 return { headers: { // 示例:Authorization: `Bearer ${localStorage.getItem('token')}` ...headers } }; }); this._apollo.create({ link: authLink.concat(httpLink), cache: new InMemoryCache() }); } onBlurZIP(zip: string) { // 改用GraphQL变量传递参数,避免直接拼接字符串(更安全、符合最佳实践) const zipQuery = gql` query GetAddressByPostalCode($postalCode: String!) { getAddressByPostalCode(postalCode: $postalCode) { country city street streettype state stateInitials quarter } } `; // 先取消之前的订阅,避免内存泄漏 this.querySubscription?.unsubscribe(); // 复用已创建的客户端发起查询 this.querySubscription = this._apollo.watchQuery<any>({ query: zipQuery, variables: { postalCode: zip }, fetchPolicy: 'network-only' }) .valueChanges .subscribe( res => { // 处理返回的地址数据 console.log('查询结果:', res.data.getAddressByPostalCode); }, err => console.error('查询出错:', err) ); } ngOnDestroy(): void { // 组件销毁时取消订阅,防止内存泄漏 this.querySubscription?.unsubscribe(); } }
HTML代码无需修改
<input formControlName="zip" (blur)="onBlurZIP($event.target.value)" type="text" class="form-control" name="zip" id="zip" required>
关键说明
- 单例客户端:Apollo客户端设计为复用的单例,重复创建会导致实例冲突,所以只在组件初始化时创建一次即可。
- GraphQL变量:把
zip通过变量传递而不是直接拼到查询字符串里,既能避免注入风险,也更符合GraphQL的规范。 - 订阅管理:每次发起新查询前取消旧订阅,组件销毁时也取消订阅,这是Angular组件中避免内存泄漏的重要习惯。
内容的提问来源于stack exchange,提问作者rbenvenuto
相关产品推荐
相关产品推荐

