1.服务的介绍和创建
注意:其实这个服务的概念有点类似于vue的provide+inject的概念,也有点类似于react的context的概念!
都是获取公共的方法
但是我们这里是全局的获取,更加强大!
1、ng g service services/storage //用命令创建服务
2、app.module.ts 里面引入创建的服务 并且声明 // ——> 如果使用了@Injectable就可以不在模块里面声明了!!!
import { StorageService } from './services/storage.service'
providers: [StorageService]
3、在用到的组件里面
//引入服务
import { StorageService } from '../../services/storage.service';
//初始化:public必须有
constructor(public storage:StorageService) {
let s=this.storage.get();
console.log(s);
}
/*
不推荐的初始化方式:
var storage = new StorageService();
console.log(storage);
*/
2.服务的声明方式
有两种常见的方式可以声明服务:
- 在providers数组中声明服务: ——> 一般不需要这样,多余的操作!所以是可选的
注意:如果使用了这种,那么@Injectable()里面是空的就可以了,但是也必须得有@Injectable()!
在Angular模块的providers
数组中声明服务,以便该服务可以在该模块及其子模块中使用。
import { NgModule } from '@angular/core';
import { YourService } from './path-to-your-service.service';
@NgModule({
declarations: [
// ... components and directives
],
providers: [YourService], // Declare the service here
})
export class YourModule { }
- **使用@Injectable装饰器声明服务: **——> 一般这个是必须有的,而且一般是使用root属性!
使用@Injectable
装饰器在服务类上标记服务,并将其提供给Angular应用程序的根模块或特定模块。
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root', // Declare the service for the entire app
})
export class YourService {
// Service implementation
}
providedIn: 'root'
表示该服务是一个应用程序级别的单例,可以在整个应用程序中使用。
无论你选择哪种方式,确保在需要使用该服务的组件中将其注入。通常使用构造函数注入(constructor injection)来注入服务。
- 上面两种方式具体的选择:
一句话:@Injectable必选,providers可选!
在Angular中,@Injectable
和在模块的providers
数组中声明服务不是互斥的,可以选择一种或两种方式来声明服务,具体取决于你的应用程序结构和需求。
通常情况下,如果你希望服务是应用程序级别的单例并且可在整个应用程序中共享,可以使用@Injectable({ providedIn: 'root' })
方式。如果你想限定服务在特定模块及其子模块中使用,可以再将其添加到模块的providers
数组中。
需要注意的是,如果同时使用了这两种方式,@Injectable
的设置会覆盖模块中的providers
数组设置,因此建议只设置@Injectable即可。
- 注意:实际上我们还可以在组件中声明一个服务 ——> 可选的
在组件中使用providers
数组,将服务添加到组件级别的providers中,这样该服务就只在该组件及其子组件中可用。
import { Component } from '@angular/core';
import { YourService } from './path-to-your-service.service';
@Component({
selector: 'app-your-component',
templateUrl: './your-component.component.html',
providers: [YourService], // Declare the service here for this component
})
export class YourComponent {
// Component implementation
}
通过这种方式,YourService
服务将仅在YourComponent
及其子组件中可用,而不会在其他组件中可用。这种方法适用于在特定组件及其子组件中使用一个特定的实例化的服务。
providedIn的取值有哪些?
providedIn
是 @Injectable
装饰器中的一个选项,用于指定服务应该由哪个模块或根级别提供。
providedIn
可以接受以下几种取值:
'root': 表示该服务是应用程序级别的单例,可在整个应用程序中共享。
ts@Injectable({ providedIn: 'root', }) export class YourService { // Service implementation }
这时服务的状态是共享的!(在 providedIn: 'root' 的情况下)
在 Angular 中,服务的状态是共享的,这意味着同一个服务实例会在整个应用中共享数据和状态。
当你在 Angular 应用中注入一个服务并使用它时,无论在哪个组件或模块中注入,都会使用同一个服务实例。这是因为 Angular 的依赖注入容器会负责创建服务的单一实例,并确保该实例在整个应用中共享。
这种共享状态的机制在很多情况下非常有用,特别是当你需要在不同的组件之间共享数据或状态时。通过将数据存储在服务中,你可以确保所有使用该服务的组件都可以访问相同的数据,而无需复杂的数据传递或状态管理。
需要注意的是,这种共享状态的机制也带来了一些潜在的问题,例如当服务中的状态需要在不同组件之间进行同步时,你需要小心处理并避免不必要的副作用。在某些情况下,你可能需要使用 RxJS 或其他状态管理工具来更精确地控制共享状态的变化和通知组件。
'any': 表示该服务将在每个模块中创建独立的实例,每个模块都有自己的服务实例。
ts@Injectable({ providedIn: 'any', }) export class YourService { // Service implementation }
特定模块类: 可以指定一个特定的模块类,以便该模块提供该服务。这样,服务将在指定模块及其子模块中共享。
tsimport { YourModule } from './path-to-your-module'; @Injectable({ providedIn: YourModule, }) export class YourService { // Service implementation }
模块类的构造函数: 可以指定一个模块类的构造函数,该构造函数将返回一个模块类,服务将在该模块及其子模块中共享。
tsimport { YourModule } from './path-to-your-module'; @Injectable({ providedIn: () => YourModule, }) export class YourService { // Service implementation }
这些选项提供了灵活的方式来指定服务的提供范围,可以根据应用程序的需要选择适当的选项。
3.服务的引入(在组件中)
使用构造函数初始化服务的原理:即constructor(public storage:StorageService)
通过构造函数参数注入了一个名为 storage
的服务 StorageService
。这是 Angular 中依赖注入(Dependency Injection)的一种用法。
这里简要解释一下代码的原理:
- 依赖注入:Angular 使用依赖注入机制来管理组件和服务之间的依赖关系。当一个组件需要使用某个服务时,你可以在组件的构造函数中声明该服务作为参数。Angular 会负责在组件初始化时创建服务的实例,并将其传递给组件。
- 构造函数参数:在构造函数的参数列表中,你声明了一个名为
storage
的参数,它的类型是StorageService
。这告诉 Angular 在创建组件实例时,需要注入一个StorageService
的实例。 - 依赖注入容器:Angular 框架会维护一个依赖注入容器,用于创建、管理和注入各种服务。当你在组件的构造函数中声明了依赖时,Angular 会自动查找或创建
StorageService
的实例,并将其传递给组件的构造函数。 - 使用服务:一旦你在组件中拥有了
storage
服务的实例,你可以调用它的方法或访问其属性,以便在组件中执行特定的业务逻辑。在你的代码中,你调用了storage
服务的get
方法,并将结果记录在变量s
中。
总之,Angular 的依赖注入机制使得组件可以方便地使用各种服务,而无需手动创建这些服务的实例。这提高了代码的可维护性和可测试性,同时也促使组件之间的松耦合,使代码更容易扩展和维护。
4.案例:todolist实现数据持久化
storage.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root' //单例模式,全局可用
})
export class StorageService {
sharedData: string = ''; //注意:也可以定义公共的数据
constructor() { }
set(key:string,value:any){
localStorage.setItem(key,JSON.stringify(value));
}
get(key:string){
//新版本的Angular中注意判断:tempStr如果是空会报错,所以需要分情况进行值的返回,不能只写return localStorage.getItem(key);
let tempStr=localStorage.getItem(key);
if(tempStr){
return JSON.parse(tempStr)
}else{
return null;
}
}
remove(key:string){
localStorage.removeItem(key);
}
}
todolist.component.ts
public todolist:any[]=[];
constructor(public storage:StorageService) {
}
ngOnInit() { //页面刷新会触发这个生命周期函数
var todolist:any=this.storage.get('todolist');
if(todolist){ //注意判断,不为空的时候才可以赋值,不然会报ts类型的错误
this.todolist=todolist;
}
}
doAdd(e:any){
if(e.keyCode==13){
if(!this.todolistHasKeyword(this.todolist,this.keyword)){
this.todolist.push({
title:this.keyword,
status:0 //0表示代办事项 1表示已完成事项
});
this.keyword='';
this.storage.set('todolist',this.todolist); //用到this一定要注意this指向(用到普通函数、回调函数一般使用箭头函数,因为箭头函数不会改变 this 的上下文,它会继承外部函数的 this;而普通的函数默认情况下会将 this 指向全局对象,通常是 window 对象。这是 JavaScript 中的默认行为。)
}else{
alert('数据已经存在');
this.keyword='';
}
}
}
deleteData(key:any){
this.todolist.splice(key,1);
this.storage.set('todolist',this.todolist);
}
用到this一定要注意this指向!
用到普通函数、回调函数(forEach、Promise、setTimeout都常常用到回调函数)一般使用箭头函数,因为箭头函数不会改变 this 的上下文,它会继承外部函数的 this;
而普通的函数默认情况下会将 this 指向全局对象,通常是 window 对象。这是 JavaScript 中的默认行为。
5.服务调用的总结
1.任意组件都可以调用服务
如果只是把服务声明在app模块下(可以不声明),在自定义模块里面也是可以引入并调用的,那是因为我们配置了root属性
2.服务里面无法调用组件
3.组件和组件之间无法互相调用方法,但是可以使用父子组件传值等方式通信
4.服务与服务之间可以相互调用
在angular中,一般不会像vue一样在main.js里面配置挂载全局的插件
一般我们把插件放在服务里面构建,然后在组件里面引入并使用即可实现全局引入的效果!!!
比如echarts、axios等插件都是可以放在服务里面的!
6.依赖注入
1.什么是依赖注入
在Angular中,依赖注入(Dependency Injection,简称DI)是一种设计模式,用于有效地管理组件、服务以及其他类之间的依赖关系。它允许你在需要的地方注入依赖项,而不必在内部显式地创建或实例化这些依赖项。
具体来说,Angular的依赖注入系统允许你声明你的组件、指令、服务等所依赖的其他类(服务),然后由Angular框架负责在需要时将这些依赖项传递给你的类。
这个概念的核心思想是:你不需要自己创建你所需要的依赖项,而是将这个责任委托给Angular框架,它会自动处理依赖项的创建和传递。
具体来说,你可以在组件、指令、服务等类的构造函数中声明你所需要的依赖项。然后,在组件被实例化时,Angular会自动为你创建这些依赖项的实例,并将它们传递给你的类。
通俗理解:实际上依赖注入的好处就是不用我们自己new了,而是在运行时angular帮我们new好对象传进到我们的构造器中供我们使用!
示例:
import { Component } from '@angular/core';
import { DataService } from './data.service';
@Component({
selector: 'app-example',
template: '<p>{{ data }}</p>',
})
export class ExampleComponent {
data: any;
// 使用依赖注入,在构造函数中声明需要的依赖项
constructor(private dataService: DataService) {
this.data = this.dataService.getData();
}
}
在这个示例中,ExampleComponent
构造函数中声明了一个依赖项 dataService
,并在构造函数中使用它。Angular会自动创建 DataService
的实例并将其传递给 ExampleComponent
。
依赖注入使得应用程序更具可维护性、可测试性和可扩展性,同时降低了组件间的耦合度。
2.在一个类里面使用另一个服务(依赖注入的使用)
在一个普通的 TypeScript 类中使用另一个 Angular 服务,你可以手动创建该服务的实例并调用其方法或访问其属性。然而,这种方法并不推荐,因为 Angular 的服务一般都依赖于 Angular 的依赖注入系统,使用它们的实例直接实例化可能会导致一些问题,并且破坏了依赖注入的好处。
如果你需要在一个普通 TypeScript 类中使用 Angular 服务,你可以通过以下两种方式来实现:
通过依赖注入: 如果这个普通的 TypeScript 类也是 Angular 服务的一部分,你可以将其他服务注入到该类的构造函数中。
tsimport { Injectable } from '@angular/core'; import { AnotherService } from './path-to-another-service.service'; @Injectable({ providedIn: 'root', }) export class YourService { constructor(private anotherService: AnotherService) { } someMethod() { this.anotherService.doSomething(); } }
手动实例化服务: 如果这个普通的 TypeScript 类不是 Angular 服务的一部分,你可以手动实例化需要使用的服务。
tsimport { AnotherService } from './path-to-another-service.service'; export class YourClass { private anotherService: AnotherService; constructor() { this.anotherService = new AnotherService(); } someMethod() { this.anotherService.doSomething(); } }
需要注意的是,推荐使用第一种方法,即通过依赖注入来使用 Angular 服务,以便保持代码的一致性、可维护性和测试性。
3.@Injectable的作用
@Injectable
是Angular框架中的装饰器(Decorator),用于标记一个类,以使该类可以被Angular的依赖注入系统所管理。
具体来说,@Injectable
的作用有以下几个方面:
依赖注入: 允许类在其构造函数中声明依赖项,这些依赖项可以由Angular的依赖注入系统注入进来。这样可以方便地管理组件、服务等之间的依赖关系。
——> 进入
在 Angular 中,如果你要在一个类中注入其他服务,你需要确保被注入的服务类上也有
@Injectable()
装饰器。这是因为 Angular 的依赖注入系统需要能够识别哪些类是可以注入的服务,而@Injectable()
提供了这样的标识。提供服务: 标记一个类为可注入的服务,使得该类可以被注入到其他组件或服务中,实现可重用、可维护的代码。——> 出去
当你创建一个 Angular 服务时,通常会在该服务类上加上
@Injectable()
装饰器,以便能够通过 Angular 的依赖注入系统来注入该服务到其他组件或服务中。也就是说@Injectable()标志着一个类是可进可出的,可以被注入,也可以注入到其他类!
优化性能: Angular会通过
@Injectable
为标记了该装饰器的服务生成优化的工厂代码,以提高应用程序的性能。
注意:实际上在组件中和在服务中使用服务,都是@Injectable依赖注入的一种实现!
而创建服务,是@Injectable提供服务的实现!
@Injectable
可以用于以下情况:
服务类: 当你创建一个服务类时,通常会在该类上使用
@Injectable
装饰器,以便该服务可以被注入到其他组件或服务中。——> 所以说服务类的核心原理也是依赖注入!tsimport { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root', }) export class YourService { // Service implementation }
注意:实际上服务类也不是必须加@Injectable,但是推荐一定要加!
如果一个服务类没有加
@Injectable
装饰器,它仍然可以正常工作,但可能会导致一些意外的行为或不符合预期的结果。在较新版本的Angular中,它们会发出警告,以提醒你最好加上@Injectable
装饰器。具体来说,如果一个服务类没有
@Injectable
装饰器,可能会遇到以下问题:- 依赖注入失败: 如果你在该服务的构造函数中声明了其他依赖项,而没有加上
@Injectable
装饰器,Angular的依赖注入系统将无法识别这个类作为一个可以被注入的服务,从而导致依赖注入失败。 - 未被优化的性能: Angular的
@Injectable
装饰器用于优化性能,如果未加装饰器,可能会影响Angular对服务的优化。
- 依赖注入失败: 如果你在该服务的构造函数中声明了其他依赖项,而没有加上
中间件: 有时候,在Angular应用程序中你可能会创建中间件,也可以使用
@Injectable
标记这些中间件类,使得它们可以被注入到其他类中。tsimport { Injectable } from '@angular/core'; @Injectable() export class YourMiddleware { // Middleware implementation //中间的类,做一些操作 }
总的来说,@Injectable
装饰器是Angular中非常重要的一部分,它使得依赖注入、服务提供和性能优化变得更加容易和高效。
在组件里面使用服务为什么不需要添加@Injectable(),就可以实现依赖注入呢?
在 Angular 中,无论是在服务中还是在组件中,要想使用依赖注入,你都需要在相应的类上添加 @Injectable()
装饰器。
然而,在组件中,你通常不需要显式添加 @Injectable()
装饰器,因为 @Component()
装饰器本身隐含了 @Injectable()
。