发布于 2026-01-06 1 阅读
0

Angular 中的服务被销毁时

Angular 中的服务被销毁时

封面照片由 Asa Rodger 拍摄,来自 Unsplash

如果我们阅读Angular OnDestroy生命周期钩子 API 的描述,就会发现它可以用于服务。
但除此之外,我们找不到更多信息。如果我们查看生命周期钩子页面,会发现上面没有任何解释,所以是时候自己尝试一下了。

我们如何使用服务?

首先,我们将深入探讨服务的使用方式。服务可以通过不同的方式提供给应用程序:

根级别

该服务对所有人开放,每个模块都可以使用此根服务。它以单例模式提供,因此仅存在一次,并且对该服务的所有操作、所有可观察对象都在整个应用程序中共享。

可以通过两种方式定义它。使用 providedIn 时,您无需在模块中提供它。



@Injectable({ providedIn: 'root'})
export class RootService { ... }


Enter fullscreen mode Exit fullscreen mode

关于提供的旁注:

您也可以使用provideIn: module,但这仍处于实验阶段,所以请谨慎使用,providedIn: 'root'它是一个很好的集成工具。

或者将其注入到根模块中



@Injectable()
export class RootService { ... }

@NgModule({
    providers: [ RootService ],
})
export class AppModule{}


Enter fullscreen mode Exit fullscreen mode

模块层面

我们还可以在模块级别创建服务,这些服务是私有的,专用于特定的模块。它们的逻辑仅在该模块内部共享。



@Injectable()
export class ModuleService { ... }

@NgModule({
    providers: [ ModuleService ],
})
export class MyCustomModule{}


Enter fullscreen mode Exit fullscreen mode

在组件层面

这些服务是在组件级别注入的,因此只有在组件运行期间它们才会存在。



// providedIn
@Injectable()
export class Componentervice { ... }

@Component({
    selector: 'my-component',
  template: '...',
    providers: [ Componentervice ],
})
export class MyComponent{}


Enter fullscreen mode Exit fullscreen mode

服务的生命周期。

首先,我们关注的是服务何时被创建。我们可以为此添加一个简单的测试。只需编写类似下面的代码即可:



export class MyService {
    constructor(){
        console.log('constructor initialized')
  }
}


Enter fullscreen mode Exit fullscreen mode

为了测试这个逻辑,我们有一些测试用例,需要在服务上创建服务。

  • 根级
  • 模块级别
  • 延迟加载模块
  • 组件级

初始化服务

所有服务均在首次使用时初始化。

关于服务的补充说明。仅仅提供服务是不够的,我们还需要实际执行一些操作来初始化该服务,因此将其添加到提供者数组中并不能初始化该服务。我们可以通过不同的方式首次使用该服务,例如通过组件或工厂来使用它……

因此,我们可以确定,所有服务都会在首次​​使用时初始化。
这也意味着,延迟加载的服务仅在路由首次加载时初始化,`providedIn: 'root'` 服务仅在首次使用时初始化,……

我们来看一下输出结果,这里出现了一个有趣的模式。正如预期的那样,根服务提供者、模块服务提供者和延迟加载服务提供者都只创建了一次。当我们重新创建组件时,服务不会再次创建。
替代文字
替代文字
替代文字

但最有趣的部分是组件提供的服务:每次创建组件时,服务也会同时创建。
替代文字

每次创建组件时,只会创建组件提供程序。

销毁服务

现在我们做同样的事情,但这次我们使用 ngOnDestroy 钩子来销毁服务。
出于某些原因,我们在 Google 中勾选了“保留日志”复选框,这样即使我们离开页面,也能跟踪日志历史记录。

同样,每个服务的测试代码都类似。



@Injectable()
export class MyService implements OnDestroy {
  name = 'xxx service';

  constructor(private counterService: CounterService) { 
    console.log('Construct the xxx service')
  }  

  ngOnDestroy() {   
    console.log('Destroy the xxx service')
  }
}


Enter fullscreen mode Exit fullscreen mode

首先我们来看组件服务,由于每次创建组件服务时都会创建组件服务,我们期望组件销毁时组件服务也会销毁。
替代文字

如果我们查看其他三项服务,就会发现它们永远不会被销毁。即使我们离开页面也不会。虽然您在离开页面时可能会意外获得它们,但这并不能保证一定如此。
替代文字

所以关于 ngOnDestroy hook 的结论是:

结论

  • 除非您在组件级别提供服务,否则您永远无法确定服务是否已被销毁。
  • 如果你提供的是组件级别的服务,并且你重构了代码,那么你需要注意这一点。

在服务中使用此钩子时要小心

完整的试用版可在 StackBlitz 上找到:

文章来源:https://dev.to/bo/when-a-service-got-destroyed-in-angular-1bd5