Angular Service(服务)
<p> 官方认为组件不应该直接获取或保存数据, 它们应该聚焦于展示数据,而把数据访问的职责委托给某个服务。而服务就充当着数据访问,逻辑处理的功能。把组件和服务区分开,以提高模块性和复用性。通过把组件中和视图有关的功能与其他类型的处理分离开,可以让组件类更加精简、高效,这是官方的一些定义。非常认同,以我粗鄙的个人开发经验来看,现实开发中并不能完全的把组件和服务区分开来。并没有做到为组件提供专门的服务,或者说把逻辑处理都放在服务,组件只展示数据。一个模块通常有许多组件,我们的每个业务模块中只存在一个服务,将模块中的数据访问,数据处理判断,通用的方法放在服务中而已。组件中还是会有一些数据的判断,页面的展示逻辑等。当然我们也有服务提供给各个组件使用,一些通用的服务 比如 httpService logService uiServic 等等。可能我说的是针对于业务上等服务无法完全剥离吧。<br><br></p><h3>1.依赖注入</h3>
<ul>
<li>
<p>注入器是主要的机制。Angular 会在启动过程中为你创建全应用级注入器以及所需的其它注入器。你不用自己创建注入器。</p>
</li>
<li>
<p>该注入器会创建依赖、维护一个容器来管理这些依赖,并尽可能复用它们。</p>
</li>
<li>
<p>提供商是一个对象,用来告诉注入器应该如何获取或创建依赖。</p>
</li>
</ul>
<h3 id="providers">2.服务提供商</h3>
<p> 我们使用命令ng g s servicename创建一个服务,在新建的服务中我们可以看到@Injectable()装饰器,它把这个类标记为依赖注入系统的参与者之一。组件中如何使用服务呢,必须将服务依赖注入系统、组件或者模块,才能够使用服务。我们可以用<strong>注册提供商</strong>和<strong>根注入器</strong>实现<strong>。</strong></p>
<p> 该服务本身是 CLI 创建的一个类,并且加上了 <code>@Injectable()</code> 装饰器。默认情况下,该装饰器是用 <code>providedIn</code> 属性进行配置的,它会为该服务创建一个提供商。在这个例子中,<code>providedIn: 'root'</code> 指定 Angular 应该在根注入器中提供该服务,从而实现<strong>根注入器</strong>将服务注入,它就在整个应用程序中可用了<strong>。</strong></p>
<p>testService.ts</p>
<div class="cnblogs_code">
<pre>import { Injectable } <span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">@angular/core</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
@Injectable({
<span style="color: rgba(255, 0, 0, 1)"> providedIn: </span></span><span style="color: rgba(255, 0, 0, 1)">'root'</span><span style="color: rgba(0, 0, 0, 1)">,
})
export </span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> TestService {
}</span></pre>
</div>
<p>也可以指定某个服务只有在特定的模块中提供,类似于一个业务模块中专属于这个模块的服务,只会应用于此模块中,我们就可以这么做。</p>
<div class="cnblogs_code">
<pre>import { Injectable } <span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">@angular/core</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
import { TestModule } </span><span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">./test.module</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
@Injectable({
<span style="color: rgba(255, 0, 0, 1)">providedIn: TestModule,</span>
})
export </span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> TestService {
}</span></pre>
</div>
<p>or</p>
<div class="cnblogs_code">
<pre>import { NgModule } <span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">@angular/core</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
import { TestService } </span><span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">./test.service</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
@NgModule({
providers: ,
})
export </span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> TestModule {
}</span></pre>
</div>
<p>也可以直接在某个组件中注入服务。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">@Component({
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> . . . </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
providers:
})</span></pre>
</div>
<h3>3.服务的作用域</h3>
<p> 为什么一个服务而已,有多种注入的方法有什么区别吗,有。这就在于这个服务作用于哪里,用于限定服务使用的界限。当我们将某个服务根注入意味着在整个应用中都可以使用,注入于某个模块,只能应用于某个模块,注入于组件中,只应用于此组件。我们根据对服务的功能定义,来选择合适的注入方式,以提高性能。</p>
<h3>4.单例服务</h3>
<p>提供单例服务的方法:</p>
<ul>
<li>
<p>把 <code>@Injectable()</code> 的 <code>providedIn</code> 属性声明为 <code>root</code>。</p>
</li>
</ul>
<div class="cnblogs_code">
<pre>import { Injectable } <span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">@angular/core</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
@Injectable({
providedIn: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">root</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
})
export </span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> TestService {
}</span></pre>
</div>
<ul>
<li>
<p>把该服务包含在 <code>AppModule</code> 或某个只会被 <code>AppModule</code> 导入的模块中。</p>
</li>
</ul>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">@NgModule({
...
providers: ,
...
})</span></pre>
</div>
<p><strong>forRoot() 模式</strong></p>
<p> 如果模块同时定义了 providers (服务),当你在多个特性模块中加载此模块时,这些服务就会被注册在多个地方。这会导致出现多个服务实例,并且该服务的行为不再像单例一样 。有多种方式来防止这种现象:</p>
<ul>
<li>用 providedIn 语法代替在模块中注册服务的方式。</li>
<li>把你的服务分离到它们自己的模块中。</li>
<li>在模块中分别定义 forRoot() 和 forChild() 方法。</li>
</ul>
<p id="using-providedin"> 使用 forRoot() 来把提供商从该模块中分离出去,这样你就能在根模块中导入该模块时带上 providers ,并且在子模块中导入它时不带 providers。</p>
<p>ps: <code>RouterModule</code> 没有 <code>forRoot()</code></p>
<pre><span>GreetingModule.ts</span></pre>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> forRoot(config: TestServiceConfig): ModuleWithProviders {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> {
ngModule: GreetingModule,
providers: [
{provide: TestServiceConfig, useValue: config }
]
};
}</span></pre>
</div>
<p>导入 GreetingModule,并只在 AppModule 中调用一次它的 forRoot() 方法。像这样注册它一次就可以防止出现多个实例。</p>
<div class="cnblogs_code">
<pre>import { BrowserModule } <span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">@angular/platform-browser</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
import { NgModule } </span><span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">@angular/core</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> App Root </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
import { AppComponent } </span><span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">./app.component</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> Feature Modules </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
import { ContactModule } </span><span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">./contact/contact.module</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
import { GreetingModule } </span><span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">./greeting/greeting.module</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> Routing Module </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
import { AppRoutingModule } </span><span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">./app-routing.module</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
@NgModule({
imports: [
BrowserModule,
ContactModule,
GreetingModule.forRoot({userName: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">Miss Marple</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">}),
AppRoutingModule
],
declarations: [
AppComponent
],
bootstrap:
})
export </span><span style="color: rgba(0, 0, 255, 1)">class</span> AppModule { }</pre>
</div>
<p><strong>工作原理:</strong></p>
<p>在 <code>GreetingModule.ts</code> 中,我们可以看到,添加一个用于配置 <code>UserService</code> 的 <code>forRoot()</code> 方法,可选的注入 Test<code>ServiceConfig</code> 扩展了 Test<code>Service</code>。如果 Test<code>ServiceConfig</code> 存在,就从这个配置中设置用户名。</p>
<p>test.service.ts (constructor)</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">content_copyconstructor(@Optional() config: Test<code>ServiceConfig</code>) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (config) { <span style="color: rgba(0, 0, 255, 1)">this</span>._userName =<span style="color: rgba(0, 0, 0, 1)"> config.userName; }
}</span></pre>
</div>
<p><strong>如何有效的防止重复导入:</strong></p>
<p>只有根模块 AppModule 才能导入 GreetingModule。如果一个惰性加载模块也导入了它, 该应用就会为服务生成多个实例。</p>
<p>为 <code>GreetingModule</code> 添加构造函数,该构造函数要求 Angular 把 GreetingModule 注入它自己。 如果 Angular 在当前注入器中查找 GreetingModule,这次注入就会导致死循环,但是 @SkipSelf() 装饰器的意思是 "在注入器树中层次高于我的祖先注入器中查找 GreetingModule。",能有效的防止重复的导入。</p>
<p>greeting.module.ts</p>
<div class="cnblogs_code">
<pre>import { ModuleWithProviders, NgModule, Optional, SkipSelf } <span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">@angular/core</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
import { CommonModule } </span><span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">@angular/common</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
import { GreetingComponent } </span><span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">./greeting.component</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
import { UserServiceConfig } </span><span style="color: rgba(0, 0, 255, 1)">from</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">./user.service</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
@NgModule({
imports: [ CommonModule ],
declarations: [ GreetingComponent ],
exports: [ GreetingComponent ]
})
export </span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> GreetingModule {
constructor (@Optional() @SkipSelf() parentModule: GreetingModule) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (parentModule) {
</span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Error(
</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">GreetingModule is already loaded. Import it in the AppModule only</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
}
}
</span><span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> forRoot(config: UserServiceConfig): ModuleWithProviders {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> {
ngModule: GreetingModule,
providers: [
{provide: UserServiceConfig, useValue: config }
]
};
}
}</span></pre>
</div>
<p> </p>
<p>此随笔乃本人学习工作记录,如有疑问欢迎在下面评论,转载请标明出处。</p>
<p>如果对您有帮助请动动鼠标右下方给我来个赞,您的支持是我最大的动力。</p><br><br>
来源:https://www.cnblogs.com/huangenai/p/12214806.html
頁:
[1]