一文彻底搞懂providedIn的所有选项,除了root还能选择啥

文章详细解释了Angular中`providedIn`选项的不同值(root、any、null、platform和Type)的作用,以及它们在组件间共享服务、懒加载模块和急性加载模块中的行为。
摘要由CSDN通过智能技术生成

当我们在创建 angular 的服务时,providedIn的选项默认都是root,有的小伙伴就会问了,这个providedIn是做什么的,除了 root 还能填其他值嘛 🤔。

查阅angular(v15)文档,发现providedIn总共有 5 种值

  • root:在大多数应用程序中是指应用程序级注入器。
  • any:在每个惰性加载的模块中提供一个唯一实例,而所有热切加载的模块共享一个实例。
  • null:等效于 undefined 。可注入物不会在任何范围内自动提供,必须添加到@NgModule@Component@Directiveproviders 数组中。
  • platform:由页面上所有应用程序共享的特殊单例平台注入器。
  • Type<any> - 将可注入物与 @NgModule 或其他 InjectorType 相关联。此选项已弃用。

angular 官网的文档一如既往的不给力 😳,只有选项,并没有示例说明,下面我们通过几个具体的例子,来看看这几个选项具体的表现 😎

root

providedIn 最常见的就是 root,因为服务创建时,默认值都是 root

root 就是在项目根目录创建一个实例,所有注入这个服务的组件,都会共享这个实例。
利用这个特性,我们经常使用它去做组件间的传值,或者做全局的传值。

我们按照下面步骤,创建一个示例

  1. 创建 2 个 module, 分别命名为 root-firstroot-second,并且使用路由懒加载
  2. 创建rootService, providedInroot, 创建变量count
  3. 给 2 个 moudlue 中的 component 都引入rootService, 显示变量count,并且添加+按钮使count + 1
import { Component, NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { RouterModule } from "@angular/router";
import { Injectable } from "@angular/core";

@Injectable({
  providedIn: "root",
})
export class RootService {
  count = 0;
  constructor() {}
}

// 第一个module
@Component({
  selector: "root-first",
  template: `
    <div>
      root-first: {{ service.count }}
      <button (click)="service.count = service.count + 1">+</button>

      <a [routerLink]="'/root-second'"> second </a>
    </div>
  `,
  styles: [""],
})
export class RootFirstComponent {
  constructor(public service: RootService) {}
}

@NgModule({
  imports: [
    RouterModule,
    RouterModule.forChild([
      {
        path: "",
        component: RootFirstComponent,
      },
    ]),
  ],
  declarations: [RootFirstComponent],
})
export class RootFirstModule {}

// 第二个module
@Component({
  selector: "root-second",
  template: `
    <div>
      root-second: {{ service.count }}
      <button (click)="service.count = service.count + 1">+</button>

      <a [routerLink]="'/root-first'"> first </a>
    </div>
  `,
  styles: [""],
})
export class RootSecondComponent {
  constructor(public service: RootService) {}
}

@NgModule({
  imports: [
    RouterModule,
    RouterModule.forChild([
      {
        path: "",
        component: RootSecondComponent,
      },
    ]),
  ],
  declarations: [RootSecondComponent],
})
export class RootSecondModule {}

// app component, app module
@Component({
  selector: "app-root",
  template: ` <router-outlet></router-outlet> `,
  styles: [``],
})
export class AppComponent {}

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    RouterModule,
    RouterModule.forRoot([
      {
        path: "root-first",
        loadChildren: () =>
          import("./app.module").then((val) => val.RootFirstModule),
      },
      {
        path: "root-second",
        loadChildren: () =>
          import("./app.module").then((val) => val.RootSecondModule),
      },
    ]),
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

在这里插入图片描述

如图,我们可以看到,在 root-first 中改变变量的值,在 root-second 中一样会起效,同样在 root-second 中改值,root-first 也会生效。

any

每个懒加载的模块中会分别共享一个实例,而所有非懒加载的模块共享一个实例

什么意思呢,同样是上面的代码,我们把 service 中的providedIn改为any,这个时候,在 root-first 中改变值,root-second 中的值就不会变化了

@Injectable({
  providedIn: "any",
})
export class RootService {
  count = 0;
  constructor() {}
}

在这里插入图片描述

这个时候如果我们把 root-first 改为急性加载,并在 app.component 中也注入 root.service

import { Component, NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { RouterModule } from "@angular/router";
import { Injectable } from "@angular/core";

@Injectable({
  providedIn: "any",
})
export class RootService {
  count = 0;
  constructor() {}
}

// 第一个module
@Component({
  selector: "root-first",
  template: `
    <div>
      root-first: {{ service.count }}
      <button (click)="service.count = service.count + 1">+</button>

      <a [routerLink]="'/root-second'"> second </a>
    </div>
  `,
  styles: [""],
})
export class RootFirstComponent {
  constructor(public service: RootService) {}
}

@NgModule({
  imports: [
    RouterModule,
    RouterModule.forChild([
      {
        path: "root-first",
        component: RootFirstComponent,
      },
    ]),
  ],
  declarations: [RootFirstComponent],
})
export class RootFirstModule {}

// 第二个module
@Component({
  selector: "root-second",
  template: `
    <div>
      root-second: {{ service.count }}
      <button (click)="service.count = service.count + 1">+</button>

      <a [routerLink]="'/root-first'"> first </a>
    </div>
  `,
  styles: [""],
})
export class RootSecondComponent {
  constructor(public service: RootService) {}
}

@NgModule({
  imports: [
    RouterModule,
    RouterModule.forChild([
      {
        path: "",
        component: RootSecondComponent,
      },
    ]),
  ],
  declarations: [RootSecondComponent],
})
export class RootSecondModule {}

// app component, app module
@Component({
  selector: "app-root",
  template: `
    app header: {{ service.count }}
    <button (click)="service.count = service.count + 1">+</button>
    <router-outlet></router-outlet>
  `,
  styles: [``],
})
export class AppComponent {
  constructor(public service: RootService) {}
}

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    RouterModule,
    RootFirstModule,
    RouterModule.forRoot([
      // {
      //   path: 'root-first',
      //   loadChildren: () =>
      //     import('./app.module').then((val) => val.RootFirstModule),
      // },
      {
        path: "root-second",
        loadChildren: () =>
          import("./app.module").then((val) => val.RootSecondModule),
      },
    ]),
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

这个时候我们发现,在 app.componentroot-firstcount 是共享的 🤓,但是 root-second 中的 count 是不与他们共享的, 这是因为 root-second 是懒加载的,而其他的则是急性加载所以他们会共享数据

在这里插入图片描述

null

如果我们把providedIn设为 null 或者不填,那么此时,就需要在 module 中的 providers 注入这个服务,否则就会报错 😲

@Injectable({
  providedIn: null,
})
export class RootService {
  count = 0;
  constructor() {}
}

在这里插入图片描述

此时我们在所有用到 root.service 服务的 module 中全部注入 service, 它的表现形式和 any 一样,即在每个懒加载的模块中会分别共享一个实例,而所有非懒加载的模块共享一个实例

import { Component, NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { RouterModule } from "@angular/router";
import { Injectable } from "@angular/core";

@Injectable({
  providedIn: null,
})
export class RootService {
  count = 0;
  constructor() {}
}

// 第一个module
@Component({
  selector: "root-first",
  template: `
    <div>
      root-first: {{ service.count }}
      <button (click)="service.count = service.count + 1">+</button>

      <a [routerLink]="'/root-second'"> second </a>
    </div>
  `,
  styles: [""],
})
export class RootFirstComponent {
  constructor(public service: RootService) {}
}

@NgModule({
  imports: [
    RouterModule,
    RouterModule.forChild([
      {
        path: "root-first",
        component: RootFirstComponent,
      },
    ]),
  ],
  declarations: [RootFirstComponent],
  // 注入服务
  providers: [RootService],
})
export class RootFirstModule {}

// 第一个module
@Component({
  selector: "",
  template: `
    <div>
      root-second: {{ service.count }}
      <button (click)="service.count = service.count + 1">+</button>

      <a [routerLink]="'/root-first'"> first </a>
    </div>
  `,
  styles: [""],
})
export class RootSecondComponent {
  constructor(public service: RootService) {}
}

@NgModule({
  imports: [
    RouterModule,
    RouterModule.forChild([
      {
        path: "",
        component: RootSecondComponent,
      },
    ]),
  ],
  declarations: [RootSecondComponent],
  // 注入服务
  providers: [RootService],
})
export class RootSecondModule {}

// app component, app module
@Component({
  selector: "app-root",
  template: `
    app header: {{ service.count }}
    <button (click)="service.count = service.count + 1">+</button>
    <router-outlet></router-outlet>
  `,
  styles: [``],
})
export class AppComponent {
  constructor(public service: RootService) {}
}

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    RouterModule,
    RootFirstModule,
    // RootSecondModule,
    RouterModule.forRoot([
      {
        path: "root-first",
        loadChildren: () =>
          import("./app.module").then((val) => val.RootFirstModule),
      },
      {
        path: "root-second",
        loadChildren: () =>
          import("./app.module").then((val) => val.RootSecondModule),
      },
    ]),
  ],
  // 注入服务
  providers: [RootService],
  bootstrap: [AppComponent],
})
export class AppModule {}

platform

页面上所有应用程序共享的平台注入器的特殊单例。

创建 appMoudle2

// 第二个根模块
@Component({
  selector: "app-root2",
  template: `
    app-root2 header: {{ service.count }}
    <button (click)="service.count = service.count + 1">+</button>
  `,
  styles: [``],
})
export class AppComponent2 {
  constructor(public service: RootService) {}
}
@NgModule({
  declarations: [AppComponent2],
  imports: [BrowserModule, RouterModule],
  bootstrap: [AppComponent2],
})
export class AppModule2 {}

同时在main.ts中引入

import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";

import { AppModule, AppModule2 } from "./app/app.module";

platformBrowserDynamic()
  .bootstrapModule(AppModule)
  .catch((err) => console.error(err));

platformBrowserDynamic()
  .bootstrapModule(AppModule2)
  .catch((err) => console.error(err));

index.html中引入app-root2

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>AngularService</title>
    <base href="/" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="icon" type="image/x-icon" href="favicon.ico" />
  </head>
  <body>
    <app-root></app-root>
    <hr />
    <app-root2></app-root2>
  </body>
</html>

然后我们把 root.serviceprovidedIn设为platform

@Injectable({
  providedIn: "platform",
})
export class RootService {
  count = 0;
  constructor() {}
}

这时,我们发现虽然是 2 个根模块,但是数据还是共享了(没有实时刷新的原因,是因为分属不同根模块,没有触发脏检查)

在这里插入图片描述

ngModule

在这里我们新创建一个 ngModule,命名为RootChildModule, 服务的providedIn我们设置RootChildModule,并且在app.module中引入RootChildModule

@NgModule()
export class RootChildModule {}

@Injectable({
  providedIn: RootChildModule,
})
export class RootService {
  count = 0;
  constructor() {
    console.log("--lucky---");
  }
}

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    RouterModule,
    RootChildModule,
    RouterModule.forRoot([
      {
        path: "root-first",
        loadChildren: () =>
          import("./app.module").then((val) => val.RootFirstModule),
      },
      {
        path: "root-second",
        loadChildren: () =>
          import("./app.module").then((val) => val.RootSecondModule),
      },
    ]),
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

此时的效果与root相同,不同之处在于,只有在 component 中引入 root.service,该服务的代码才会被打包进去,否则代码将会被摇树优化。

为了验证此功能,我们把 root.service 移出,新建/child/root.service 文件

import { Injectable } from "@angular/core";
import { RootChildModule } from "./root-child.module";

@Injectable({
  providedIn: RootChildModule,
})
export class RootService {
  count = 0;
  constructor() {
    console.log("--lucky---");
  }
}

同时新建/child/root-child.module 来给 providedIn 提供值

import { NgModule } from "@angular/core";

@NgModule()
export class RootChildModule {}

然后修改一下app.module的代码

import { Component, NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { RouterModule } from "@angular/router";
import { RootChildModule } from "./child/root-child.module";

// 第一个模块
@Component({
  selector: "root-first",
  template: ` <div>root-first</div> `,
  styles: [""],
})
export class RootFirstComponent {
  constructor() {}
}
@NgModule({
  imports: [
    RouterModule,
    RootChildModule,
    RouterModule.forChild([
      {
        path: "",
        component: RootFirstComponent,
      },
    ]),
  ],
  declarations: [RootFirstComponent],
})
export class RootFirstModule {}

// app component, app module
@Component({
  selector: "app-root",
  template: `
    app header
    <router-outlet></router-outlet>
  `,
  styles: [``],
})
export class AppComponent {
  constructor() {}
}

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    RouterModule,
    RouterModule.forRoot([
      {
        path: "root-first",
        loadChildren: () =>
          import("./app.module").then((val) => val.RootFirstModule),
      },
    ]),
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

注意,此时,我们没有在任何地方注入root.service, 此时我们在浏览器 network 中搜索 root.service 中的代码

在这里插入图片描述

结果可知,代码没有打包进去

此时,我们在RootFirstComponent中注入root.service

import { RootService } from "./child/root.service";

// 第一个模块
@Component({
  selector: "root-first",
  template: ` <div>root-first</div> `,
  styles: [""],
})
export class RootFirstComponent {
  constructor(public rootService: RootService) {}
}

此时,再去 network 中搜索,发现已有代码
在这里插入图片描述

我们再删除RootFirstComponent中已注入的root.service,并且把providedIn的值改为null,并且在RootFirstModule里面申明RootService

import { Injectable } from "@angular/core";
import { RootChildModule } from "./root-child.module";

@Injectable({
  providedIn: null,
})
export class RootService {
  count = 0;
  constructor() {
    console.log("--lucky---");
  }
}
import { RootService } from "./child/root.service";

// 第一个模块
@Component({
  selector: "root-first",
  template: ` <div>root-first</div> `,
  styles: [""],
})
export class RootFirstComponent {
  constructor() {}
}

@NgModule({
  imports: [
    RouterModule,
    RootChildModule,
    RouterModule.forChild([
      {
        path: "",
        component: RootFirstComponent,
      },
    ]),
  ],
  providers: [RootService],
  declarations: [RootFirstComponent],
})
export class RootFirstModule {}

此时搜索 network, 虽然已经删除了注入部分的代码,但是 root.service 中的代码依然能搜索到
在这里插入图片描述

这里,我们不用providedIn:root举例的原因是,服务的 providedIn 为 root 时,不用声明,也能使用,所以自然代码也会被摇树优化掉,我想这也是 ngModule这个选项被弃用的原因。

总结

实际中,我们使用比较多的只有rootnull

  • root同时具备摇树优化的能力,但是他会在全局共享数据,并且没有限制,任意地方都可以使用
  • null可以根据情况,在需要的时候使用providers注入使用,并且会区分懒加载和急性加载

platform虽然有实际的应用场景,但是使用比较少,而anyngModule都可以被anyroot取代

源码

angular-injectable-providedIn

  • 46
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Python面向对象编程(Object-Oriented Programming,简称OOP)是一种程序设计方法,它将数据和操作数据的方法组合成对象,通过定义类(class)来创建对象。下面是一些概念和原则,可以帮助你更好地理解Python面向对象编程。 1. 类和对象: - 类是一种抽象的数据类型,它定义了对象的属性和方法。 - 对象是类的实例,它具有类定义的属性和方法。 2. 属性和方法: - 属性是对象的数据,可以是整数、字符串、列表等。 - 方法是对象的行为,可以是函数或过程。 3. 封装: - 封装是将数据和对数据的操作封装在一起,以创建一个独立的实体。 - 使用类来封装数据和方法,可以隐藏实现细节,提高代码的可读性和可维护性。 4. 继承: - 继承是一种机制,允许一个类继承另一个类的属性和方法。 - 子类可以重用父类的代码,并且可以添加新的属性和方法。 5. 多态: - 多态是指同一个方法可以在不同的类中具有不同的实现方式。 - 多态可以提高代码的灵活性和可扩展性。 下面是一个简单的例子,展示了如何定义一个类、创建对象并调用对象的方法: ```python class Person: def __init__(self, name, age): self.name = name self.age = age def say_hello(self): print(f"Hello, my name is {self.name} and I'm {self.age} years old.") # 创建对象 person = Person("Alice", 25) # 调用对象的方法 person.say_hello() ``` 这个例子定义了一个名为`Person`的类,它有两个属性(`name`和`age`)和一个方法(`say_hello`)。我们通过`Person`类创建了一个名为`person`的对象,并调用了它的`say_hello`方法。 希望这个简单的例子能帮助你更好地理解Python面向对象编程。如果你有其他问题,请随时提问。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值