四、Angular 服务与依赖注入

article/2025/8/13 15:38:29

一、概述

  • Angular 为了解决数据共享和逻辑复用问题,引入了服务的概念,服务简单理解就是一个带有特性功能的类,Angular 提倡把与视图无关的逻辑抽取到服务中,这样可以让组件类更加精简、高效,组件的工作只管用户体验,把业务逻辑相关功能(比如:从服务器获取数据,验证用户输入或直接往控制台中写日志等)委托给各种服务,最后通过 Angular 的依赖注入,这些带有特定功能的服务类可以被任何组件注入并使用。

  • 依赖注入 (dependency injection,DI) 是这样一个系统: 它让程序中的某部分可以访问其他部分,而且我们可以配置它们的访问方式。(可以把注入器看作new操作符的替代品)

  • Angular 的依赖注入,需要先了解下面这几个概念:

    • @Injectable() 装饰器来提供元数据,表示一个服务可以被注入的

    • 注入器(Injector):注入器是Angular 自己的类,不需要用户创建,它负责提供依赖注入功能Angular 中的注入器是树状结构,按照层级划分,有根注入器、模块注入器(Module Injector)、组件注入器及元素注入器(Element Injector)。Angular 会在Web 应用程序启动过程中创建注入器。注入器是一个容器,它会创建依赖,并管理这些依赖,使这些依赖在 Web 应用程序中的其他地方也可使用

    • @Inject() 装饰器表示要在组件或者服务中注入一个服务

    • 提供者(Provider):提供者是一个类,用来告诉注入器应该如何获取或创建依赖。对要用到的任何服务,Angular 都要求必须至少注册一个提供者。

    • 依赖:依赖描述的是一个类从外部源接收它需要的对象实例,这些对象实例作为依赖已经在注入器中创建好了,该类仅需要注入它即可使用,不需要自己创建这些依赖。依赖可以是服务类函数、对象、接口或值等。

  • 最常见的情况是提供一个服务或值,它将在整个应用中保持一致。在我们的应用中,99%的场景可能都属于这种情况。

  • 通过依赖注入管理和分发的对象被称为服务(service),一般将类定义在名为 xxx.service.ts 的文件中。
    在这里插入图片描述

二、创建可注入的服务类

  • 在Angular 中创建服务类与创建模块、组件、指令类似。默认情况下,使用Angular CLI的 ng generate service 命令创建服务,它会生成一个用 @Iniectable()装饰器声明的服务类,如创建一个日志服务。

    # ng generate service log 的缩写,log是服务类的文件名
    ng g service log 
    
  • 上述命令会生成服务类文件 src/app/log.service.ts,文件初始内容如下

    import { Injectable } from '@angular/core';@Injectable({providedIn: 'root'
    })
    export class LogService {constructor() { }
    }
    
  • Angular 的服务类用 @lnjectable() 装饰器声明。@lnjectable() 装饰器是一个标记性装饰器,它声明的类可由注入器创建并可以作为依赖项注入。它仅包含一个 providedIn 属性的元数据providedIn 属性用于指定注入器,可接收 3 种字符串: root、platform 和any。它们分别代表着3种不同级别的注入器。

三、选择注入器

  • Angular 提供的注入器有多种,Angular 在启动过程中会自动为每个模块创建一个注入器,注入器是一个树结构。
    • (1) Angular 为根模块 (AppModule )创建的是根注入器。
    • (2) 根注入器会提供依赖的一个单例对象,可以把这个单例对象注入多个组件中。
    • (3) 模块和组件级别的注入器可以为它们的组件及其子组件提供同一个依赖的不同实例。
    • (4) 可以为同一个依赖使用不同的提供者来配置这些注入器,这些提供者可以为同一个依赖提供不同的实现方式。
    • (5) 注入器是可继承的,这意味着如果指定的注入器无法解析某个依赖,它就会请求父注入器来解析它。组件可以从它自己的注入器中获取服务、从其祖先组件的注入器中获取服务、从其父模块(NgModule 类)级别的注入器中获取服务,或从根注入器中获取服务。
  • 服务有作用域,表示该服务在 Web 应用程序中的可见性。不同级别的注入器创建的依赖服务对应着 Web 应用程序中的不同可见性。@Iniectable() 装饰器提供了选择注入器的一种方式,即通过配置元数据 providedIn 属性的值,可以选择不同级别的注入器。
  • 字符串 root 表示选择的是根注入器,根注入器在整个 Web 应用程序中仅创建服务的一个单例对象,可以把这个单例对象注入任何想要它的类中。
  • 字符串 platform 表示选择的是元素注入器,元素注入器使服务可以在整个 Web 应用程序和Angular 自定义元素间共享。关于 Angular 自定义元素,简单地理解就是将 Angular 组件的所有功能打包为原生的 HTML,可以供其他非 Angular 框架使用。因此,platform 作用域大于 root作用域。
  • 字符串 any 表示选择的是模块注入器,这意味着同一服务可能有多个实例。它与 root 的区别如下。
    • 对非延迟加载的模块,它们共享一个由根注入器提供的实例。
    • 对延迟加载的模块,每个模块分别创建一个自己的实例,供模块内单独使用。
  • 选择了注入器后,依赖注入还需要一个提供者,因为 Angular 对要用到的任何服务,都要求必须至少注册一个提供者。对服务类来说,提供者就是它自己。因此,上节 (第二章 创建可注入的服务类) 使用命令创建的LogService 服务已经是一个依赖服务了,可以在其他组件中注入并使用它。

四、配置提供者

  • 提供者就做了两件事:(1) 告诉注入器如何提供依赖值,(2) 限制服务可使用的范围
  • 当使用提供者配置注入器时,注入器就会把提供者和一个 token 关联起来,维护一个内部关系(token-Provider)映射表,当请求一个依赖项时就会引用它。token 就是这个映射表的键。
  • 在 Angular 依赖注入系统中,用户可以根据名字在缓存中查找依赖,也可以通过配置过的提供者来创建依赖。我们必须使用提供者来配置注入器,否则注入器就无法知道如何创建此依赖。注入器创建服务实例的最简单方法,就是用这个服务类本身来创建它。但是在现实中,依赖除了服务类外,还可以是函数、对象、接口或值等。因此,Angular 提供了很多类型的提供者,不同的提供者可以针对特定的依赖项提供定制化的创建服务;如对于服务类来说,也可以通过配置提供者来定制化创建服务实例。

[1]. 提供者的类型

  • 提供者是一个实现了 Provider 接口的对象,它告诉注入器应该如何获取或创建依赖的服务实例。提供者类型定义如下。

    type Provider = TypeProvider | ValueProvider | classProvider | ConstructorProvider | ExistingProvider | FactoryProvider | any[];
    

[2]. 配置方法

  • 在 Angular 中有3个地方可以配置提供者,以便在 Web 应用程序的不同层级使用提供者来配置注入器。

    • 在服务本身的 @Injectable()装饰器中。
    • 在模块类的 @NgModule()装饰器中。
    • 在组件类的 @Component()装饰器中。
  • 其中在服务本身的 @Injectable()装饰器中仅有一个元数据的 providedIn 属性,用户可以用它来选择不同的注入器,配置提供者的属性为默认值。其实对服务来说,提供者就是它自己。因此Angular 默认通过调用该服务类的 new 运算符来创建服务实例。

  • 在 @NgModule()装饰器和 @Component()装饰器的元数据中都提供了 providers 属性,用户可以通过该属性来配置提供者。

    // my-app.module.ts
    @NgModule({declarations: [ MyAppComponent,], providers: [ MyService ] // <--- 这里// 等同于 // providers: [{ provide: MyComponent, useClass: MyComponent }]
    }) class MyAppModule {}// my-app.component.ts
    export class MyAppComponent { constructor(private myService: MyService) { // do something with myService here } 
    }
    
  • 当使用 @NgModule() 装饰器中的 providers 属性配置提供者时,该服务实例对该 NgModule类中的所有组件是可见的,该 NgModule 类中的所有组件都可以注入它。

  • 当使用 @Component()装饰器中的 providers 属性配置提供者时,该服务实例只对声明它的组件及其子组件可见,它会为该组件的每一个新实例提供该服务的一个新实例。

1). TypeProvider
  • TypeProvider 称为类型提供者,类型提供者用于告诉注入器使用指定的类来创建服务实例,本质上是通过调用类的 new 运算符来创建服务实例。这也是我们用得最多的一种提供者。具体代码如下。

    providers:[LogService ]// LogService是一个由@Injectable()装饰器声明的类
    
  • 在上面的代码中,配置的依赖项是一个LogService类的服务实例,而该类的类型LogService 是该依赖的 token 值。

2). ValueProvider
  • ValueProvider 称为值提供者,ValueProvider接口定义如下。

    interface ValueProvider extends ValueSansProvider {provide: anymulti?:boolean //可选参数useValue: any
    }
    
  • 其中的 provide 属性接收3种类型的 token 值:类、InjectionToken 对象实例及其他任何对象实例。

  • 当 provide 属性的 token 值为类和对象实例时,参考下面的代码片段。

    const JAVA_BOOK = new Book('Learning Java','Java');
    providers:{// 注入的依赖为字符串值,string类作为该依赖的token值{provide:string, useValue:'Hello'}, // 注入的依赖为字符串值,字符串对象实例name作为该依赖的token值{provide:'name', useValue:'He1lo'}, // 注入的依赖为Book对象实例,Book对象实例作为该依赖的token值{provide:Book, useValue:JAVA_BOOK} 
    }
    
  • InjectionToken 类用来创建 InjectionToken 对象实例,该类的定义如下

    class InjectionToken<T>{//接收一个泛型(T)对象constructor(desc:string, //一个描述( desc)参数options?:{ providedIn?: Type<any> | "root" | "platform" | "any"; factory:()=>T;}protected _desc:stringtoString():string
    }
    
  • InjectionToken 类接收一个泛型对象和一个描述参数。当 provide 属性为 InjectionToken 对象实例时,useValue 属性接收的类型取决于 InjectionToken 类中的泛型对象类型。

    // 创建一个字符串类型的可注入对象
    Const HELLO_MESSAGE= new InjectionToken<string>('He1lo!');
    providers:[{provide: HELLO_MESSAGE,useValue:'Hello World!'//接收一个字符串,与InjectionToken类的泛型对象string对应
    }]
    
  • Angular 中的接口其实是 TypeScript的功能,而 JavaScript 没有接口,所以当 TypeScript转译成 JavaScript时,接口也就消失了。因此,|njectionToken 类常用于封装接口类型的对象实例,代码如下。

    // Config是一个接囗apiEndPoint:string;
    interface Config {apiEndPoint: string;timeout:number;
    }//定义一个接口类型实例
    const configValue:Config={apiEndPoint: 'def.com',timeout: 5000
    };
    //定义一个InjectionToken类对象实例,实际是封装config接口
    const configToken = new IniectionToken<Config>('demo token');
    providers:[{provide:configToken, useValue:configValue //使用configToken作为依赖的token值
    }]
    
3). ClassProvider
  • ClassProvider 称为类提供者,ClassProvider与 ValueProvider类似,它的 provide 属性接收值与 ValueProvider 提供者相同,不同的是 useClass 属性接收一个类,或者该类的子类。

    providers:[{provide: LogService,useClass:LogService
    }]
    
  • 在上面的代码中,依赖项的值是一个LogSenvice 类的实例,而该类的类型 LogService 是该依赖的 token 值。

4). ConstructorProvider
  • ConstructorProvider 可以理解为等同 TypeProvider,它仅有 provide 属性,且接收一个类。

    providers:[{provide: LogService}]
    
  • 在上面的代码中,依赖项的值是一个 LogService 类的实例,而该类的类型 LogService 是该依赖的 token 值。

5). ExistingProvider
  • ExistingProvider用于创建别名提供者。假设老的组件依赖于 OldLogger 类。OldLogger 类和 NewLogger 类的接口相同,但是由于某种原因,我们没法修改老的组件来使用 NewLogger类,这时可以使用 useExisting 为 OldLogger 类指定一个别名 NewLogger。

    [NewLogger, {provide: OldLogger,useExisting: NewLogger
    }]
    
6). FactoryProvider
  • 有时候可能需要动态创建依赖值,创建时需要的信息要等运行期间才能获取。这时可以使用FactoryProvider。 FactoryProvider 使用 useFactory 属性来配置该注入器。useFactory 属性接收一个函数。

    @NgModule({declarations: [ DiSampleApp ], imports: [ BrowserModule ], bootstrap: [ DiSampleApp ],providers:[{provide: LogService,useFactory: ()=> new LogService()}]
    })
    
  • 在上面的代码中,依赖项的值是 useFactory属性中的函数返回的对象实例,LogSenvice 类是该依赖的 token 值。

  1. 创建一个新的项目
    ng new demo-serve --routing --css --inimal
    
  2. 创建一个 user 组件
    ng g c user  
    
  3. 创建 user 模型
    ng g m user
    
  4. 创建服务类用于实现本地存储的需求
    ng g s service/storage
    

五、在类中注入服务

  • 当完成了 Angular 依赖注入的配置后,注入器通过提供者创建依赖,创建依赖的过程可以这么理解:注入器将查找具体 token 值对应的提供者,然后使用该提供者创建对象实例,作为依赖项存储在注入器中。当完成了依赖的创建后,我们就可以通过依赖注入的方式,在 Angular 中使用该依赖的服务实例的方法和属性了。
  • 上面提到过,选择不同的注入器,或在不同的位置配置提供者,服务在 Web 应用程序中的可见性是不同的。如 providedin 属性配置为 root 时,服务是单例的;也就是说,在指定的注入器中最多只有某个服务的一个实例。
  • Angular 的依赖注入具有分层注入体系,Web 应用程序有且只有一个根注入器。这意味着下级注入器也可以创建它们自己的服务实例。Angular 会有规律地创建下级注入器。每当 Angular 创建一个在@Component) 装饰器中指定了 providers 属性的组件实例时,它也会为该组件实例创建一个新的子注入器。同样,当在运行期间加载一个新的 NgModule 类时,Angular 也可以为它创建一个拥有自己的提供者的注入器。
  • 子模块和组件注入器彼此独立,并且会为所提供的服务分别创建自己的服务实例。当 Angular销毁 NgModule 类或组件实例时,也会销毁这些注入器和注入器中的那些服务实例。
  • 子组件注入器是其父组件注入器的子节点,它会继承所有的祖先注入器,其终点则是 Web 应用程序的根注入器。

[1] 注入依赖类实例

  • 我们可以通过类的构造函数注入依赖类,即在构造函数中指定参数的类型为注入的依赖类。下面的代码是某个组件类的构造函数,它要求注入 LogService 类。

    constructor(private logService: LogService)
    
  • 注入器将会查找 token 值为 LogService 类的提供者,然后将该提供者创建的对象实例作为依赖项,赋值给 logService 变量。当完成了依赖注入后,用户就可以在类中使用该服务实例的方法和属性了。

[2] 注入可选的依赖类实例

  • 在实际应用中,有时候某些依赖服务是可有可无的,换句话说,可能存在需要的依赖服务找不到的情况。这时,我们可以通过 @Optional() 装饰器来显式地声明依赖服务,告知 Angular 这是一个可选的依赖服务。同时,我们需要通过代码中的条件来判断依赖服务是否存在,代码如下。

    constructor(@Optional() private logService: LogService) [if (this.logService) { // 判断是否存在// 具体业务逻辑}
    }
    

[3] 使用 @Inject() 装饰器指定注入实例

  • [1] 注入依赖类实例小节在介绍注入依赖类实例时,注入的类型是该依赖类的类型,即可以理解为根据token 值来注入依赖类。当我们遇到注入的依赖类是一个值对象、数组或者接口的情况时,需要使用 @lnject() 装饰器来显式地指明依赖类的 token 值。如之前我们使用 IniectionToken 类封装了一个接口类型的依赖,然后期望在组件或者服务类中注入该接口类型的依赖时,需要使用 @Injiect() 装饰器来显式地指明依赖的 token 值,代码如下。

    // 注意这里需要使用@Inject() 装饰器 configToken 是该接口依赖的token值
    // Config是注入依赖的类型
    constructor(@Inject(configToken) private config: Config) { console.log('new instance is created');
    }
    
  • 我们在服务中配置一个值提供者,然后在类中注入该值提供者创建的值

    providers: [{provide: 'name'.useValue: '变量name的值'
    }]
    // String是注入依赖的类型
    constructor(@Inject('name') private config: String){this.title = '值Provide:' + config;
    }
    

[4] 注入Injector 类对象实例

  • Iniector 类是 Angular 的注入器对应的 Class 类。既然注入器创建和维护着依赖,那么我们可以直接注入 Injector 类对象实例,然后通过它的方法获取依赖。上面介绍的注入值类型,可以用下面的方式实现。

    providers: [{provide: 'name'useValue: '变量name的值'
    }]
    // 注入Injector类对象实例
    constructor(private injector: Injector) {this.title = '值Provide:' + injector.get('name');
    }
    
  • 在上述代码中,我们通过注入Injector类对象实例,然后调用它的 get() 方法来获取对应的依赖。

六、用例

  1. 创建一个新的项目

    ng new demo-indection --routing --minimal
    
  2. 创建两个延迟加载模块

    这里不再说明,可以查看我的angular 路由文章第六章

    ng g m features/employee --route employee --module app.module
    
    ng g m features/department --route department --module app.module
    
  3. 创建配置接口和依赖服务类

    # interface  可以简写 i
    # 新建接口 src/app/shared/config.ts
    ng g interface shared/config 
    # 新建服务类 src/app/shared/config.service.ts
    ng g service shared/config
    
  4. 编辑接口类 src/app/shared/config.ts

    import { InjectionToken } from "@angular/core";export interface Config {apiEndPoint: string;timeout: number;
    }export const configToken = new InjectionToken<Config>("demo token");
    
  5. 编辑依赖服务类 src/app/shared/config.service.ts

    import { Injectable, InjectionToken, Inject } from "@angular/core";
    import { Config, configToken } from "./config";//通过 @Injectable() ​装饰器标记为可以被注入的服务
    @Injectable({// 表示当前服务在 Root 注入器中提供,// 简单理解就是这个服务在整个应用所有地方都可以注入,并全局唯一实例。providedIn: "root",
    })
    export class ConfigService {// 注入 Config 接口对象constructor(@Inject(configToken) private config: Config) {console.log("创建一个新的用例");}getValue() {return this.config;}
    }
    
  6. 编辑 employee 组件 src/app/features/employee/employee.component.ts

    import { Component, OnInit } from "@angular/core";
    import { ConfigService } from "src/app/shared/config.service";@Component({selector: "app-employee",template: ` <p>employee works!</p> `,styles: [],
    })
    export class EmployeeComponent implements OnInit {// 通过构造函数注入服务constructor(private configService: ConfigService) {}ngOnInit(): void {console.log(this.configService.getValue());}
    }
    
  7. 编辑 employee 模块 src/app/features/employee/employee.module.ts

    import { NgModule } from "@angular/core";
    import { CommonModule } from "@angular/common";import { EmployeeRoutingModule } from "./employee-routing.module";
    import { EmployeeComponent } from "./employee.component";
    import { Config, configToken } from "src/app/shared/config";export const configValue: Config = {apiEndPoint: "abc.com",timeout: 5000,
    };@NgModule({declarations: [EmployeeComponent],imports: [CommonModule, EmployeeRoutingModule],providers: [{provide: configToken,useValue: configValue, // 注册 ValueProvider},],
    })
    export class EmployeeModule {}
    
  8. 编辑 department组件 src/app/features/department/department.component.ts

    import { Component, OnInit } from "@angular/core";
    import { ConfigService } from "src/app/shared/config.service";@Component({selector: "app-department",template: ` <p>department works!</p> `,styles: [],
    })
    export class DepartmentComponent implements OnInit {constructor(private configService: ConfigService) {}ngOnInit(): void {}
    }
    
  9. 编辑 department 模块 src/app/features/department /department .module.ts

    import { NgModule } from "@angular/core";
    import { CommonModule } from "@angular/common";import { DepartmentRoutingModule } from "./department-routing.module";
    import { DepartmentComponent } from "./department.component";
    import { Config, configToken } from "src/app/shared/config";// 自定义配置
    export const configValue: Config = {apiEndPoint: "xyz.com",timeout: 4000,
    };@NgModule({declarations: [DepartmentComponent],imports: [CommonModule, DepartmentRoutingModule],providers: [{provide: configToken,useValue: configValue,},],
    })
    export class DepartmentModule {}
    
  10. 编辑根组件 src/app/app.component.ts

    import { Component } from "@angular/core";@Component({selector: "app-root",template: `<a routerLink="/employee">雇员</a><br /><a routerLink="department">部门</a><router-outlet></router-outlet>`,styles: [],
    })
    export class AppComponent {title = "demo-indection";
    }
    
  11. 这时,如果允行项目,点击页面的链接,控制台抛出错误信息,产生空指针错误,详细情况是组件中注入的 ConfgService 类遇到了空指针错误。

    • 产生错误信息的原因是,我们在 ConfigService 类中默认使用了 providedln:root' 配置,它表示将 ConfigService 类注入根注入器中,并在 Web 应用程序的启动阶段就实例化好了一个实例对象。然而 ConfigService 类的配置参数是分别在延迟加载模块中注册的,当我们准备单击页面上的链接时,两个延迟加载模块并没有加载,因此 ConfigService 类的配置参数这时不存在,故ConfigService 类遇到了空指针错误。准确地说,它依赖的 configToken 对象为空。
    • 解决这个问题的思路是,可以在 AppModule 根模块中初始化 configToken 对象,然后将其注册到根注入器中,这时 ConfigService 类在启动阶段就能读取配置参数了。
  12. 编辑根模块 src/app/app.module.ts

  • 配置完成后,再次单击页面链接,控制台均输出 AppModule 根模块中配置的内容,说明两个延迟加载模块加载同一个配置参数。但是我们期望的是在不同的延迟加载模块中加载不同的配置参数。这时,我们可以通过配置 providedIn:'any’来达成此目的。
    import { BrowserModule } from "@angular/platform-browser";
    import { NgModule } from "@angular/core";import { AppRoutingModule } from "./app-routing.module";
    import { AppComponent } from "./app.component";
    import { Config, configToken } from "./shared/config";export const configValue: Config = {apiEndPoint: "def.com",timeout: 5000,
    };@NgModule({declarations: [AppComponent],imports: [BrowserModule, AppRoutingModule],providers: [{// 注册 ValueProviderprovide: configToken,useValue: configValue,},],bootstrap: [AppComponent],
    })
    export class AppModule {}
    
  1. 编辑根模块 src/app/shared/config.service.ts

    import { Injectable, InjectionToken, Inject } from "@angular/core";
    import { Config, configToken } from "./config";@Injectable({providedIn: "any", // 原先的值是 root
    })
    export class ConfigService {// 注入 Config 接口对象constructor(@Inject(configToken) private config: Config) {console.log("创建一个新的用例");}getValue() {return this.config;}
    }
    
  2. 配置完成后,再次单击页面链接,控制台分别输出各自延迟加载模块中配置的内容,说明两个延迟加载模块加载的是不同的配置参数。

    • 通过上述示例我们可以看出,合理地使用提供者,可以使 Web 应用程序模块化和参数化。

五、创建依赖

  • 上面我们介绍了如何通过注入Iniector 类对象实例获取依赖。同样,我们也可以直接使用Injector 类创建依赖。

    constructor(){const injector = Injector.create({ providers: [{ provide: 'name', useValue:'变量name的值'}]});this.title = '值Provide:'+ injector.get('name');
    }
    
  • 上述代码通过 Injector 类的 create()方法,创建了一个 Injector 类对象实例,该对象实例中维护着一个 token 值为 name 的值提供者,该值提供者负责创建依赖(一个值对象 )。用户通过调用Injector 类对象实例的 get()方法获得对应值提供者创建的依赖。


http://www.hkcw.cn/article/sDgcRkTeve.shtml

相关文章

源码:Spring常规Bean创建过程

Bean创建过程&#xff1a; 一、版本 5.3.10二、学习内容 Bean创建过程源码三、Bean生命周期 时间轴地址&#xff1a;点击 四、bean创建过程脑图总结 脑图地址&#xff1a;点击 五、源码过程 说明&#xff1a; bean创建入口一般都是通过getBean(xxx);方法进入的&#xf…

用ENSP完成SSH登录实验(设备间登录),学起来学起来!!

最近才用到SSH的登录功能&#xff0c;简单的学习总结。大佬勿喷&#xff01;&#xff01;&#xff01; 用ENSP完成SSH登录实验&#xff08;设备间相互登录&#xff09;&#xff0c;学起来学起来&#xff01;&#xff01; 具体思路&#xff1a; 设备连线&#xff0c;启动。配…

调整图片和表格尺寸的命令:resizebox

\resizebox 是 LaTeX 中的一个命令&#xff0c;用于调整插入的内容&#xff08;如图像、表格、文本等&#xff09;的大小。它的语法如下&#xff1a; \resizebox{<width>}{<height>}{<content>}其中&#xff1a; <width> 和 <height> 分别表示…

Linux练级宝典->动态库和静态库

动静态库的原理 我们知道可执行文件前的4步骤 预编译->编译->汇编->链接 预处理&#xff1a; 完成头文件展开、去注释、宏替换、条件编译等&#xff0c;最终形成xxx.i文件。编译&#xff1a; 完成词法分析、语法分析、语义分析、符号汇总等&#xff0c;检查无误后将…

uniapp在自定义tabbar上动态修改svg图标颜色和字体颜色

需求:在uniapp项目内,自定义tabbar,需要将图标更换成svg格式,可动态修改图标及字体颜色。 效果图如下: 我使用的是uniapp结合uview2的组件使用,代码如下: <u-tabbar :value="currentIndex" @change="tabbarChange" :fixed="true" :a…

三、Angular 路由

一、简介 Angular 的路由服务是一个可选的服务&#xff0c;它用来呈现指定的 URL 所对应的视图。它并不是Angular 核心库的一部分&#xff0c;而是位于 angular/router 包中。像其他 Angular 包一样&#xff0c;路由服务在用户需要时才从此包中导入。 [1]. 创建路由模块 默认…

C语言——字符函数和字符串函数(二)

&#x1f4dd;前言&#xff1a; 上一篇文章C语言——字符函数和字符串函数&#xff08;一&#xff09;对字符函数和字符串函数strlen&#xff0c;strcpy和strncpy&#xff0c;strcat和strncat进行了初步的讲解 这篇文章主要再讲解几个我们常用到的其他字符串函数&#xff08;附…

【C语言字符函数和字符串函数(二)】--strcmp,strstr的使用和模拟实现,strncpy,strncat,strncmp函数的使用,strock,strerror函数的使用

目录 一.strcmp的使用和模拟实现 1.1--strcmp的使用演示 1.2--strcmp的模拟实现 二.strstr的使用和模拟实现 2.1--strstr的使用演示 2.2--strstr的模拟实现 三.strncpy函数的使用 3.1--strncpy的代码演示 3.2--strncpy的模拟实现 3.3--比较strcpy和strncpy函数 四.…

白玉兰奖综艺类别入围名单公布 奖项即将揭晓

第30届上海电视节白玉兰奖入围名单在综艺类别中已经公布,最终获奖结果将在6月27日的“白玉兰绽放”颁奖典礼上揭晓。责任编辑:zhangxiaohua

曝理想汽车计划打造家庭轿车 或对标保时捷Panamera

据多方消息,国内造车新势力领军企业理想汽车正计划进军家庭轿车市场,其首款轿车产品疑似对标知名豪华品牌保时捷的Panamera车型。在最近的一季度财报会上,理想汽车CEO李想透露了公司进入轿车市场的计划。他表示,在现有增程SUV和纯电MPV产品线的市场表现达到预期后,将根据市…

t002-在线装修管理系统的设计与实现

项目视频演示 摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对信息管理混乱&#xff0c;出错率高&#xff0c;信息…

vue3 各种数据处理

1.对数组去重, 并取出想要的值 用途&#xff1a;获取此数组中共有几个名字 方法&#xff1a;...new Map&#xff0c;map,values() 示例&#xff1a; let aar [{id: 1, name: name1, value: value1},{id: 1, name: name1, value: value2},{id: 2, name: name2, value: value…

广西瓜农因收购价低把西瓜扔进池塘 市场饱和致价格偏低

5月29日,广西扶绥县的瓜农因西瓜滞销和收购价低而面临困境。一些瓜农选择将西瓜扔进池塘或任其烂在地里。山圩镇的一名瓜农表示,今年西瓜的收购价仅为每斤两毛钱,远低于往年水平,预计自己将亏损四五万元,地里有约10万斤西瓜被丢弃。一位西瓜收购商指出,收购价确实较低,这…

德国真会向乌提供金牛座导弹吗 态度摇摆不定

德国总理默茨上任以来,在是否向乌克兰提供“金牛座”远程巡航导弹问题上的态度一直摇摆不定。他在26日参加活动时提到,英国、法国、德国和美国“已不再限制援助乌克兰武器的射程”,这一言论引发了外界关于德国是否已经批准向乌克兰提供“金牛座”导弹的各种猜测。路透社报道…

孙俪唇下痣系因车祸玻璃碴致假性痣“孙俪唇下痣没了”

5月28日,演员孙俪出席时尚芭莎年度派对时,人们注意到她唇下的痣不见了。随后,孙俪工作室发布了一组照片,证实了这一点。两天后,“孙俪唇下痣没了”这一话题登上热搜。早在《甄嬛传》播出期间,孙俪唇下的小黑点就引起了观众的关注。到了《芈月传》和《那年花开月正圆》播出…

17岁学生登顶珠峰保送清华?学校回应 并无此类招生计划

近日,多家媒体报道称北京第八十中学17岁的学生李浩榕成为中国首位从北坡登顶珠峰的青少年,并登上热搜。然而,根据国内媒体报道,河北16岁女孩丁禹琪在2020年也曾从北坡成功登顶珠峰。西藏登山协会证实,在中国业余登山爱好者中,李浩榕是珠峰北坡登顶的最年轻男性,丁禹琪是…

确定男女关系第二天 她就开始借钱...

今年年初,郑先生谈了一个女朋友,期间女方向他借了多笔钱。现在两人分手后,郑先生希望能把钱要回来。郑先生是四川人,今年40岁,离异两年,独自抚养一个孩子。他在杭州从事物流运输工作已有六年。郑先生找到记者是因为和一名女子产生了纠纷。该女子向他借了12万元,承诺每月…

这些“牛奶”不仅浪费钱还没营养 减肥路上的“绊脚石”

早餐奶、核桃奶、巧克力奶,这些“牛奶”你还在喝吗?如果购买的牛奶包装上标有“调制乳”或“含乳饮料”,那么它们可能是减肥路上的绊脚石,不仅浪费钱还缺乏营养。调制乳是以不低于80%的生牛(羊)乳或复原乳为主要原料,添加其他原料或食品添加剂或营养强化剂制成。例如早餐…

【QT】理解QT机制之“元对象系统”

目录 前置知识&#xff1a; &#xff08;1&#xff09;C运行时多态 &#xff08;2&#xff09;RTTI QT的元对象系统 1.元对象系统基本内容 2.元对象代码 3.元对象系统其它特性 前置知识&#xff1a; 在理解Qt的元对象系统之前&#xff0c;有必要理解C的动态多态相关知识…

男子蹭2天爱心小面顿顿2斤多 暖心之举遭遇“羊毛党”

上海面馆老板为困难人士提供免费重庆小面,男子连蹭两天“爱心小面”,顿顿吃两斤多。老板:我们也是农民,我们也很辛苦,你这样子我们也吃不消。” 暖心老板提供免费面被薅羊毛狂吃。责任编辑:zhangxiaohua