@Injectable装饰器标记类可在依赖注入系统中使用,通过providedIn:'root'使其成为应用级单例服务。Angular根据构造函数参数类型与emitDecoratorMetadata生成的元数据,在运行时自动识别注入目标并完成服务装配与依赖解析,实现高效、自动化的依赖管理。
先来看两个 SAP 电商云 Spartacus UI 里的 Angular Service 类,它们都加上了 @Injectable 注解,唯一的区别在于有没有 providedIn 这个参数:

长期稳定更新的攒劲资源: >>>点此立即查看<<<

@Injectable() 装饰器的作用很直接——告诉 Angular:这个类可以在依赖注入系统中使用。换句话说,它是让服务具备“可被注入”资格的入口。
如果加上 providedIn: 'root',那就意味着这个服务在整个应用范围内都是可见的,任何组件或服务都能直接请求它,而不必在模块里手动注册。
那么,当我们在组件里使用某个服务时,Angular 是怎么知道该注入哪个实例的呢?答案藏在构造函数的参数类型里。来看一个典型例子:
import { Component } from '@angular/core';
import { Http } from '@angular/http';
@Component({
selector: 'example-component',
template: 'I am a component'
})
class ExampleComponent {
constructor(private http: Http) {
// use `this.http` which is the Http provider
}
}
这里 private http: Http 的类型是 Http(注意大写 H),Angular 会自动把它配给对应的服务实例。听起来很神奇?对 JavaScript 开发者来说,确实有点“黑魔法”的味道。
要知道,类型定义是 TypeScript 特有的东西,编译成 JavaScript 后按理说应该消失得无影无踪。那浏览器里的 JavaScript 凭什么还能认出 http 参数该是什么?
秘密藏在 tsconfig.json 里的一个配置:emitDecoratorMetadata 设为 true。这个开关会把参数的类型信息作为元数据,塞进编译后的装饰器代码里。来看上面那段 TypeScript 编译后实际长什么样(为了清晰,保留了 ES6 导入):
import { Component } from '@angular/core';
import { Http } from '@angular/http';
var ExampleComponent = (function() {
function ExampleComponent(http) {
this.http = http;
}
return ExampleComponent;
})();
ExampleComponent = __decorate(
[
Component({
selector: 'example-component',
template: 'I am a component',
}),
__metadata('design:paramtypes', [Http]),
],
ExampleComponent
);
发现关键点了吗?__metadata('design:paramtypes', [Http]) 这行把 Http 类型存成了元数据。所以 @Component 装饰器在运行时会被转换成普通的 ES5 函数,而 __decorate 负责把元数据一起打包。Angular 拿到这个元数据后,就知道构造函数的第一参数应该注入 Http 令牌对应的实例,然后把它赋值给 this.http:
function ExampleComponent(http) {
this.http = http;
}
整个过程环环相扣:TypeScript 的类型信息通过 emitDecoratorMetadata 保留下拉,装饰器元数据告诉 Angular 该注入什么,最终依赖注入系统优雅地完成了服务装配。这就是 @Injectable 背后最核心的工作原理。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述