Angular(v21) 中的 Effects 与 InjectionContext

发布: (2026年2月1日 GMT+8 03:42)
4 min read
原文: Dev.to

Source: Dev.to

注入器:真相之源

注入器是一个容器,用来保存服务和依赖的实例。在 Angular 中,存在一系列层级的注入器(环境注入器、组件注入器等)。

当你创建一个 effect 时,它需要 锚定 到某个注入器。注入器为 effect 提供它可能需要的服务,更重要的是,它决定了 effect 的生命周期。

注入上下文:“何时”和“何地”

注入上下文是代码执行期间的一个特定状态,此时 inject() 函数是可用的。可以把它看作对象(组件、指令等)正在构造的那段时间。

默认情况下,你在以下位置拥有注入上下文:

  • 类的 构造函数
  • 字段初始化器(例如 myService = inject(MyService))。
  • Provider 的 工厂函数

为什么 effect 需要它?

当你在构造函数中调用 effect(() => { … }) 时,Angular 会隐式地寻找当前的注入上下文,以找到并 “注入” DestroyRef。它利用这个引用来准确地知道何时 “终止” effect,从而避免内存泄漏。

inject() 函数:现代获取器

inject() 函数是从当前注入上下文中获取依赖的编程方式。

如果你尝试在普通方法(例如按钮点击)中创建 effect,它会失败,因为该方法没有注入上下文。

// ✅ WORKS: In the constructor / class init
count = signal(0);
myEffect = effect(() => console.log(this.count()));

// ❌ FAILS: Outside injection context
updateData() {
  effect(() => { /* … */ }); // Error: inject() must be called from an injection context
}

手动捕获注入器

export class MyComponent {
  private injector = inject(Injector); // Capture the injector in the constructor

  startManualEffect() {
    // Pass the captured injector to the effect
    effect(() => {
      console.log('Manual effect running!');
    }, { injector: this.injector });
  }
}

DestroyRef:清理小组

DestroyRef 是对 OnDestroy 生命周期钩子的现代替代。它允许你在代码的任何位置注册清理逻辑,而不仅限于类定义中。

当创建 effect 时,它内部会订阅其上下文的 DestroyRef。当持有该 effect 的组件或服务被销毁时,DestroyRef 会触发,effect 自动停止。

使用 DestroyRef 手动清理

const dr = inject(DestroyRef);
const sub = mySignal.subscribe(...);

dr.onDestroy(() => {
  console.log('Cleaning up resources!');
  sub.unsubscribe();
});

runInInjectionContext 辅助函数

有时你已经拥有一个 Injector,但不想把它作为选项传递给每个函数。你可以 “强制” 创建一个上下文:

import { runInInjectionContext } from '@angular/core';

runInInjectionContext(this.injector, () => {
  // Anything called here now has access to inject()
  effect(() => console.log('I have context!'));
});

代码练习场

关键要点

  • EffectsInjector 紧密相连。
  • Injectors 使用 DestroyRef 进行清理。
  • inject() 只能在注入上下文激活时使用。
  • 如果在构造函数之外创建 effect,必须手动提供 injector。

祝编码愉快!

Back to Blog

相关文章

阅读更多 »

Angular i18n 中缺失的部分

如何在每次应用更改时防止破坏翻译,Angular 的 i18n 故事乍看之下似乎已经完整。你标记字符串,运行 ng extract-i18n,然后……