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

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>

关键说明

  1. 单例客户端:Apollo客户端设计为复用的单例,重复创建会导致实例冲突,所以只在组件初始化时创建一次即可。
  2. GraphQL变量:把zip通过变量传递而不是直接拼到查询字符串里,既能避免注入风险,也更符合GraphQL的规范。
  3. 订阅管理:每次发起新查询前取消旧订阅,组件销毁时也取消订阅,这是Angular组件中避免内存泄漏的重要习惯。

内容的提问来源于stack exchange,提问作者rbenvenuto

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 04:25:58