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

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.xdnf.cn/news/18565.html

相关文章:

  • 日志搜索系统前端页面(暂无后端功能)
  • webrtc弱网-SendSideBandwidthEstimation类源码分析与算法原理
  • 手机横屏适配方案
  • 20250823给荣品RD-RK3588开发板刷Rockchip原厂的Buildroot【linux-5.10】时调通AP6275P的WIFI【源码部分】
  • ArkTS 语言全方位解析:鸿蒙生态开发新选择
  • 【AI基础:神经网络】17、神经网络基石:从MP神经元到感知器全解析 - 原理、代码、异或困境与突破
  • 线程间Bug检测工具Canary
  • uniapp 页面跳转及字符串转义
  • Redis学习笔记 ----- 缓存
  • rust语言 (1.88) egui (0.32.1) 学习笔记(逐行注释)(八)按键事件
  • 大语言模型应用开发——利用OpenAI函数与LangChain结合从文本构建知识图谱搭建RAG应用全流程
  • 【KO】前端面试七
  • 20250823给荣品RD-RK3588开发板刷Rockchip原厂的Android14【EVB7的V10】时调通AP6275P的WIFI
  • react相关知识
  • GitLab CI:Auto DevOps 全解析,告别繁琐配置,拥抱自动化未来
  • 运行npm run命令报错“error:0308010C:digital envelope routines::unsupported”
  • 二叉树的经典算法与应用
  • 【网安干货】--操作系统基础(上)
  • USRP采集的WiFi信号绘制星座图为方形
  • 新手向:异步编程入门asyncio最佳实践
  • K8s 实战:Pod 版本更新回滚 + 生命周期管控
  • 嵌入式学习日记(33)TCP
  • 【UnityAS】Unity Android Studio 联合开发快速入门:环境配置、AAR 集成与双向调用教程
  • CMake link_directories()详细介绍与使用指南
  • STM32F1 GPIO介绍及应用
  • C/C++三方库移植到HarmonyOS平台详细教程(补充版so库和头文件形式)
  • 凌霄飞控开发日志兼新手教程——基础篇:认识基本的文件内容和相关函数作用(25电赛备赛版)
  • 【序列晋升】12 Spring Boot 约定优于配置
  • Spring发布订阅模式详解
  • Python 调用 sora_image模型 API 实现图片生成与垫图