Angular Service(服務)


      官方認為組件不應該直接獲取或保存數據, 它們應該聚焦於展示數據,而把數據訪問的職責委托給某個服務。而服務就充當着數據訪問,邏輯處理的功能。把組件和服務區分開,以提高模塊性和復用性。通過把組件中和視圖有關的功能與其他類型的處理分離開,可以讓組件類更加精簡、高效,這是官方的一些定義。非常認同,以我粗鄙的個人開發經驗來看,現實開發中並不能完全的把組件和服務區分開來。並沒有做到為組件提供專門的服務,或者說把邏輯處理都放在服務,組件只展示數據。一個模塊通常有許多組件,我們的每個業務模塊中只存在一個服務,將模塊中的數據訪問,數據處理判斷,通用的方法放在服務中而已。組件中還是會有一些數據的判斷,頁面的展示邏輯等。當然我們也有服務提供給各個組件使用,一些通用的服務 比如 httpService logService uiServic 等等。可能我說的是針對於業務上等服務無法完全剝離吧。

1.依賴注入

  • 注入器是主要的機制。Angular 會在啟動過程中為你創建全應用級注入器以及所需的其它注入器。你不用自己創建注入器。

  • 該注入器會創建依賴、維護一個容器來管理這些依賴,並盡可能復用它們。

  • 提供商是一個對象,用來告訴注入器應該如何獲取或創建依賴。

2.服務提供商

     我們使用命令ng g s servicename創建一個服務,在新建的服務中我們可以看到@Injectable()裝飾器,它把這個類標記為依賴注入系統的參與者之一。組件中如何使用服務呢,必須將服務依賴注入系統、組件或者模塊,才能夠使用服務。我們可以用注冊提供商根注入器實現

      該服務本身是 CLI 創建的一個類,並且加上了 @Injectable() 裝飾器。默認情況下,該裝飾器是用 providedIn 屬性進行配置的,它會為該服務創建一個提供商。在這個例子中,providedIn: 'root' 指定 Angular 應該在根注入器中提供該服務,從而實現根注入器將服務注入,它就在整個應用程序中可用了

testService.ts

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

@Injectable({
  providedIn: 'root',
})
export class TestService {
}

也可以指定某個服務只有在特定的模塊中提供,類似於一個業務模塊中專屬於這個模塊的服務,只會應用於此模塊中,我們就可以這么做。

import { Injectable } from '@angular/core';
import { TestModule } from './test.module';

@Injectable({
 providedIn: TestModule,
})
export class TestService {
}

or

import { NgModule } from '@angular/core';
import { TestService } from './test.service';

@NgModule({
  providers: [TestService],
})
export class TestModule {
}

也可以直接在某個組件中注入服務。

@Component({
/* . . . */
  providers: [TestService]
})

3.服務的作用域

        為什么一個服務而已,有多種注入的方法有什么區別嗎,有。這就在於這個服務作用於哪里,用於限定服務使用的界限。當我們將某個服務根注入意味着在整個應用中都可以使用,注入於某個模塊,只能應用於某個模塊,注入於組件中,只應用於此組件。我們根據對服務的功能定義,來選擇合適的注入方式,以提高性能。

4.單例服務

提供單例服務的方法:

  • 把 @Injectable() 的 providedIn 屬性聲明為 root

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

@Injectable({
  providedIn: 'root',
})
export class TestService {
}
  • 把該服務包含在 AppModule 或某個只會被 AppModule 導入的模塊中。

@NgModule({
  ...
  providers: [TestService],
  ...
})

forRoot() 模式

     如果模塊同時定義了 providers (服務),當你在多個特性模塊中加載此模塊時,這些服務就會被注冊在多個地方。這會導致出現多個服務實例,並且該服務的行為不再像單例一樣 。有多種方式來防止這種現象:

  • 用 providedIn 語法代替在模塊中注冊服務的方式。
  • 把你的服務分離到它們自己的模塊中。
  • 在模塊中分別定義 forRoot() 和 forChild() 方法。

 使用 forRoot() 來把提供商從該模塊中分離出去,這樣你就能在根模塊中導入該模塊時帶上 providers ,並且在子模塊中導入它時不帶 providers。

ps: RouterModule 沒有 forRoot()

GreetingModule.ts
static forRoot(config: TestServiceConfig): ModuleWithProviders {
  return {
    ngModule: GreetingModule,
    providers: [
      {provide: TestServiceConfig, useValue: config }
    ]
  };
}

導入 GreetingModule,並只在 AppModule 中調用一次它的 forRoot() 方法。像這樣注冊它一次就可以防止出現多個實例。

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

/* App Root */
import { AppComponent } from './app.component';

/* Feature Modules */
import { ContactModule } from './contact/contact.module';
import { GreetingModule } from './greeting/greeting.module';

/* Routing Module */
import { AppRoutingModule } from './app-routing.module';

@NgModule({
  imports: [
    BrowserModule,
    ContactModule,
    GreetingModule.forRoot({userName: 'Miss Marple'}),
    AppRoutingModule
  ],
  declarations: [
    AppComponent
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

工作原理:

在 GreetingModule.ts 中,我們可以看到,添加一個用於配置 UserService 的 forRoot() 方法,可選的注入 TestServiceConfig 擴展了 TestService。如果 TestServiceConfig 存在,就從這個配置中設置用戶名。

test.service.ts (constructor)

content_copyconstructor(@Optional() config: TestServiceConfig) {
  if (config) { this._userName = config.userName; }
}

如何有效的防止重復導入:

只有根模塊 AppModule 才能導入 GreetingModule。如果一個惰性加載模塊也導入了它, 該應用就會為服務生成多個實例。

為 GreetingModule 添加構造函數,該構造函數要求 Angular 把 GreetingModule 注入它自己。 如果 Angular 在當前注入器中查找 GreetingModule,這次注入就會導致死循環,但是 @SkipSelf() 裝飾器的意思是 "在注入器樹中層次高於我的祖先注入器中查找 GreetingModule。",能有效的防止重復的導入。

greeting.module.ts

import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
import { GreetingComponent } from './greeting.component';
import { UserServiceConfig } from './user.service';

@NgModule({
  imports:      [ CommonModule ],
  declarations: [ GreetingComponent ],
  exports:      [ GreetingComponent ]
})
export class GreetingModule {
  constructor (@Optional() @SkipSelf() parentModule: GreetingModule) {
    if (parentModule) {
      throw new Error(
        'GreetingModule is already loaded. Import it in the AppModule only');
    }
  }

  static forRoot(config: UserServiceConfig): ModuleWithProviders {
    return {
      ngModule: GreetingModule,
      providers: [
        {provide: UserServiceConfig, useValue: config }
      ]
    };
  }
}

 

此隨筆乃本人學習工作記錄,如有疑問歡迎在下面評論,轉載請標明出處。

如果對您有幫助請動動鼠標右下方給我來個贊,您的支持是我最大的動力。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM