当前位置: 首页 > news >正文

Angular初学者入门第三课——工厂函数(精品)

一、定义

在 Angular 中,工厂函数是一个返回服务或对象的函数。它被用来定义和创建服务、对象或者一些特定逻辑的实例,以方便在整个应用中复用。在 Angular 的依赖注入系统中,工厂函数是提供者(Provider)的一部分,用于创建某些对象或值,并将其注入到组件或服务中。

使用场景如下:

  • 动态配置:在运行时根据不同的环境条件返回不同的配置或服务实例
  • 复杂对象生成:需要组合多个服务或依赖项来生成一个复杂的对象时,可以使用工厂函数
  • 跨模块共享逻辑:当多个模块需要共享某些生成逻辑时,可以使用工厂函数来统一生成和管理这些逻辑

二、通过providers注入含简单参数的工厂函数

我们准备实现一个含简单参数的工厂函数来返回 service 实例,因此要先定义 service,然后再实现工厂函数。因为工厂函数的参数是简单类型,所以不需要添加到 deps 中,完整的示例如下:

export class My01Service {constructor(private config: any) { }getConfig() {return this.config;}
}
import { My01Service } from "./my01.service";export function my01ServiceFactory(environment: any): My01Service {const config = {apiUrl: environment.production ? 'https://api.prod.com' : 'http://api.dev.com',};return new My01Service(config);
}
providers: [/* 按需静态加载图标 */provideNzIcons(icons),{provide: APP_INITIALIZER,/* 项目启动时加载缓存中的主题样式 */useFactory: loadTheme,deps: [ThemeService],multi: true},{provide: My01Service, //与useFactory配合使用表示工厂函数的返回值就是My01Service类型的对象useFactory: () => my01ServiceFactory(environment)}
]

三、通过providers注入含service类型参数的工厂函数

如果工厂函数所返回的 service 实例依赖其他的 service 对象,那我们就以参数的形式把依赖的对象传给工厂函数。如果依赖的 service 对象(以CountService为例)有@Injectable({providedIn: 'root'}) 装饰器,就不需要添加到 providers、deps 中。如果依赖的 service 对象(以StringService为例)没有@Injectable({providedIn: 'root'}) 装饰器,则相反。完整的示例如下:

export class StringService {constructor() { }appendSuffix(input: string) {return input + "aaa";}
}@Injectable({providedIn: 'root'
})
export class CountService {count:number = 0constructor() { }addCount() {this.count++;console.log(`In countService: ${this.count}`)}getCount() {return this.count}
}
export class My02Service {constructor(private config: any, private stringService: StringService, private countService: CountService) { }getConfig() {let result = this.stringService.appendSuffix("01");return this.config + result;}
}
import { CountService } from "./count.service";
import { My02Service } from "./my02.service";
import { StringService } from "./string.service";export function my02ServiceFactory(environment: any, stringService: StringService, countService: CountService): My02Service {const config = {apiUrl: environment.production ? 'https://api.prod.com' : 'http://api.dev.com',};return new My02Service(config, stringService, countService);
}
providers: [StringService,/* 按需静态加载图标 */provideNzIcons(icons),{provide: APP_INITIALIZER,/* 项目启动时加载缓存中的主题样式 */useFactory: loadTheme,deps: [ThemeService],multi: true},{provide: My01Service,useFactory: () => my01ServiceFactory(environment)},{provide: My02Service, //与useFactory配合使用表示工厂函数的返回值就是My02Service类型的对象useFactory: (stringService: StringService, countService: CountService) => my02ServiceFactory(environment, stringService, countService),deps: [StringService]}
]

providers: [StringService] 等价于 providers: [{provide: StringService, useClass: StringService}]

四、export function返回匿名函数

二、三节都是通过工厂函数返回一个 service 对象,我们也可以通过工厂函数来返回一个匿名函数。这里的匿名函数跟C#中的匿名函数意思相同,都是指那些只有参数和方法体,没有方法名的函数。工厂函数本身可以接收参数,工厂函数所返回的匿名函数也可以有参数和返回值。

//export function可以在定义之前使用
loggerFactory01('add','guo')(3,5);export function loggerFactory01(environment: string, userId: string): (num01: number, num02: number) => number {return (n01: number, n02: number) => {if (environment == "add") {let result = n01 + n02;console.log(`Add result is ${result}`)return result;}else {let result = n01 - n02;console.log(`Subtract result is ${result}`)return result;}}
}

五、export const返回匿名函数

特性export functionexport const
是否支持提升(Hoisting)是,可以在定义之前使用否,必须先定义后使用
定义方式定义并命名函数定义一个常量变量(可以是箭头函数或其他值)
灵活性只能导出函数可以导出任何值,包括函数、对象、数组等
适用场景工具函数、纯函数匿名函数、模块内方法
//export const支持多种类型的导出值,不仅限于函数,还可以导出对象、数组等
export const config = {apiUrl: 'https://example.com',timeout: 5000
};export const loggerFactory02 = (environment: string): ((num01: number, num02: number) => number) => (n01: number, n02: number) => {if (environment == "multiply") {let result = n01 * n02;console.log(`Multiply result is ${result}`)return result;}else {let result = n01 / n02;console.log(`Divide result is ${result}`)return result;}}

六、使用刚才通过providers注入的service

在 app.module.ts 中注入完毕后我们就可以直接在 component 的构造函数中接收并使用这些 sercive 了。

import { Component, Inject, inject, OnInit } from '@angular/core';
import { loggerFactory01, loggerFactory02 } from '../../services/factoryFunc'
import { My01Service } from '../../services/my01.service';
import { My02Service } from '../../services/my02.service';@Component({selector: 'app-resource',standalone: true,imports: [ CommonModule ],templateUrl: './resource.component.html',styleUrls: ['./resource.component.less']
})
export class ResourceComponent implements OnInit {//为构造函数添加刚才注入的service类型参数,Angular 的 DI 系统会把相应的对象传进来constructor(private store: Store, private my01Service: My01Service, private my02Service: My02Service) { //先在组件内部import引用工厂函数再调用loggerFactory01('add','guo')(3,5);loggerFactory01('subtract','guo')(5,6);loggerFactory02('multiply')(2,3);loggerFactory02('divide')(10,2);//使用构造函数的实参进行方法调用let config01 = this.my01Service.getConfig();console.log(`config01 is ${config01}`)let config02 = this.my02Service.getConfig();console.log(`config02 is ${config02}`)}
}

七、@Injectable的用法

@Injectable 是从 Angular2 引入的功能, 用于标记一个类可以被依赖注入系统管理。通过 @Injectable 装饰器,我们可以声明一个服务或其他类为可注入的依赖项。这使得 Angular 的 DI 系统能够将该类注册到提供者(providers)中,并在需要的地方提供其实例。从 Angular6 开始引入了 providedIn。

除了第三部分中 CountService 体现的通常用法外,当需要显式指定依赖项的注入令牌(Injection Token)时,可以使用 @Injectable 装饰器。

import { Injectable, Inject } from '@angular/core';
import { API_CONFIG_IT, AppConfig } from './api-config';@Injectable()
export class MyService {constructor(@Inject(API_CONFIG_IT) private config: ApiConfig) {}getApiEndpoint(): string {return this.config.apiEndpoint;}
}
import { InjectionToken } from '@angular/core';export interface ApiConfig {apiEndpoint: string;title: string;
}export const API_CONFIG_IT = new InjectionToken<ApiConfig>('api.config');export const API_CONFIG: ApiConfig = {apiEndpoint: 'https://api.example.com',title: 'My Angular App',
}
@NgModule({providers: [{ provide: API_CONFIG_IT, useValue: API_CONFIG },],
})
export class AppModule {}

八、总结

相信各位看官认真读到这里后对工厂函数应该有一个清楚的认识和理解了,虽然说平常开发时经常会用到 @Injectable,但关于它的更底层的理论知识我们也应该了解一下,这样开发时对代码为什么这么些才会更通透。

OK,如果各位看官觉得本文对你有所帮助,请点赞、收藏、评论支持一下,我将感激不尽。

http://www.dtcms.com/a/346760.html

相关文章:

  • 游戏广告投放数据分析项目:拆解投放的“流量密码”
  • kail的浏览器连接不上网
  • 20250823给荣品RD-RK3588开发板刷Rockchip原厂的Buildroot【linux-5.10】时调通AP6275P的WIFI【源码部分】
  • 从 M4S 到 MP4:用 FFmpeg 轻松合并音视频文件
  • 达梦数据库统计信息收集
  • 无人机光伏巡检误检率↓79%!陌讯多模态融合算法在组件缺陷检测的落地优化
  • 【85页PPT】数字化转型LIMS大型企业智能制造之LIMS实验室管理系统产品解决方案(附下载方式)
  • webrtc弱网-SendSideBandwidthEstimation类源码分析与算法原理
  • 使用dism++备份系统时,出现“句柄无效”错误的解决方法
  • 重构实训生态:旅游管理虚拟仿真实训室的标准落地与价值创新
  • 某电器5G智慧工厂网络建设全解析
  • 【ABAP4】创建Package
  • 数字经济、全球化与5G催生域名新价值的逻辑与实践路径
  • Autosar CAN开发06(CAN通讯开发需求-CAN矩阵)
  • 突破传统文本切片的瓶颈:AntSK-FileChunk语义切片技术详解前言:为什么我们需要重新思考文本切片?
  • RHCSA--命令(二)
  • 一种通过模板输出Docx的方法
  • Agent Lightning:让任何AI智能体通过强化学习实现高效训练
  • 简单介绍计算机的工作过程
  • 深入理解 Linux 系统文件 I/O:从 open 到重定向的底层逻辑》
  • 力扣热题之技巧
  • 云计算核心技术之云网络技术
  • Agentic AI 知识框架整理
  • 08.23总结
  • FFMPEG相关解密,打水印,合并,推流,
  • 基于Python对酷狗音乐排行榜数据分析可视化【源码+LW+部署】
  • 安卓开发实战:从零构建一个天气应用
  • 【Android】使用FragmentManager动态添加片段
  • C# 项目“交互式展厅管理客户端“针对的是“.NETFramework,Version=v4.8”,但此计算机上没有安装它。
  • week4-[字符数组]字符统计