Angular初学者入门第二课——.ts、.d.ts、.state.ts的区别(精品)

初次接触 Angular 实际项目时,发现里边有很多不同后缀的文件,虽然没深入研究过,但根据其他编程语言的经验猜测这应该是通过后缀名来区分文件的作用。后来有时间研究了一下具体的细节和不同点,就有了今天这篇文章,这些知识虽然基础但属于必知必会的内容,希望有需要的看官可以认真读下去。

一、普通 .ts 文件

  • 普通的 .ts 文件是 TypeScript 源文件,主要用于编写程序的逻辑和功能,这些文件会被编译成JavaScript 文件,并在运行时执行
  • 包含实现代码:普通的 .ts 文件通常包含类、函数、变量的定义和实现逻辑
  • 会被编译:在构建或编译项目时,.ts 文件会被 TypeScript 编译器转换成 .js 文件
  • 使用场景:组件、服务、模块、指令等功能性代码文件
  • 这里把 .component.ts、.module.ts 都作为普通 .ts,app.component.ts 改名为 app-component.ts、app.module.ts 改名为 
    app-module.ts 后
    项目仍可以正常运行(我们一般不改它们的后缀名)
// app.component.ts
import { Component } from '@angular/core';@Component({selector: 'app-root',template: `<h1>Hello Angular!</h1>`,
})
export class AppComponent {title: string = 'Angular App';constructor() {console.log('AppComponent initialized');}
}

二、.d.ts 文件

  • .d.ts 文件是 TypeScript 的类型声明文件,用于提供类型定义信息,注意不包括具体的实现
  • 它为 TypeScript 编译器提供类型检查和代码补全的支持,主要用于定义模块、库、全局变量或接口的类型
  • 仅包含类型声明:.d.ts 文件只包含类型信息,没有实现逻辑
  • 不会被编译:TypeScript 编译器会直接使用 .d.ts 文件中的类型信息进行类型检查,但不会将他们编译成 JavaScript 文件
  • 使用场景:定义全局变量或接口;
    为没有类型定义的第三方库提供类型支持;
    为模块或库的外部用户提供类型支持;
  • 通俗点理解 .d.ts 文件中存放需要用 declare 声明的类型,global.d.ts 改成 globald.ts 编译会失败
//以下是 global.d.ts 文件的内容
declare global {interface Window {myCustomProperty: string;}
}//注意:TypeScript 声明文件必须包含至少一个 export 或 import,否则它会被视为一个全局脚本文件,而不是模块。
export {};
@Component({selector: 'app-resource',standalone: true,imports: [ CommonModule ],templateUrl: './resource.component.html',styleUrls: ['./resource.component.less']
})
export class ResourceComponent implements OnInit {constructor() { // 使用扩展的字段window.myCustomProperty = 'Hello, window.myCustomProperty!';console.log(window.myCustomProperty);}
}

三、.state.ts

  • .state.ts 文件并不是 TypeScript 本身的标准文件类型,而是通常作为项目中的一种约定,用于定义和管理状态(state)相关的代码
  • 它通常用于描述应用程序的状态模型,或者与状态管理工具(如NgRx、Akita、Redux等)配合
  • 包含状态定义和逻辑:.state.ts 文件可能包含状态模型、初始状态、状态操作和选择器等
  • 会被编译:与普通的 .ts 文件一样,.state.ts 文件也会被编译为 JavaScript 文件
  • 使用场景:通常用于 Angular 项目中的状态管理模块,帮助管理局部或全局状态
  • 既然是非标准文件类型,那么 resource.actions.ts 改名为 resource-actions.ts、resource.model.ts 改名为 resource-model.ts、resource.state.ts 改名为 resource-state.ts 后项目仍可以正常运行(我们一般不改它们的后缀名)
  • 下面的代码是一个前后端完整的 ResourceState 实例,包括:定义ResourceState、@Selector()、@Action(xxx),以及使用ResourceState 和调用 @Action(xxx)
//resource.model.ts 文件的内容
export interface Resource {id: number,name: string
}export interface ResourceStateModel {resources: Resource[];selectedResourceId: number | null;
}
//resource.actions.ts 文件的内容
import { Resource } from "./resource.model";export class AddResource {static readonly type = '[Resource] Add Resource'; //动作类型constructor(public payload: Resource) {} //传递完整的资源数据
}export class SelectResource {static readonly type = '[Resource] Select Resource'; //动作类型constructor(public payload: number) {} //传递选中资源的Id
}
//resource.state.ts 文件的内容
import { Injectable } from '@angular/core';
import { State, Action, StateContext, Selector, NgxsAfterBootstrap } from '@ngxs/store';
import { Resource, ResourceStateModel } from './resource.model';
import { AddResource, SelectResource } from './resource.actions';@State<ResourceStateModel>({name: 'resource', //状态的名称,用于标识defaults: {resources: [{id: 1,name: 'name01'},{id: 2,name: 'name02'}], //初始资源列表为空,若ngxsAfterBootstrap()中对resources重新赋值则此处的初始化数据无效,若ngxsAfterBootstrap()中没对resources重新赋值则此处的初始化数据有效selectedResourceId: 1  //初始没有选中的资源}
})@Injectable({providedIn: 'root'
})
export class ResourceState implements NgxsAfterBootstrap{//定义选择器,用于从状态中派生数据//选择所有资源@Selector()static getAllResources(state: ResourceStateModel): Resource[] {return state.resources;}//选择当前选中的资源@Selector()static getSelectedResource(state: ResourceStateModel): Resource | undefined {return state.resources.find(resource => resource.id === state.selectedResourceId);}//定义动作处理器,用于更新状态,@Action(AddResource)与第二个参数必须是同类型//动作:添加资源@Action(AddResource)addResource(ctx: StateContext<ResourceStateModel>, action: AddResource): void {console.log(`In addResource, action as below:`)console.log(action)const state = ctx.getState();/*...是扩展运算符或剩余参数,扩展运算符用于将数组或对象的元素展开为单独的值,通常用于合并数组、克隆对象等操作。示例 1:展开数组const array1 = [1, 2, 3];const array2 = [4, 5, 6];const combinedArray = [...array1, ...array2];console.log(combinedArray); // 输出:[1, 2, 3, 4, 5, 6]示例 2:展开对象const obj1 = { name: 'Alice', age: 25 };const obj2 = { location: 'New York' };const combinedObj = { ...obj1, ...obj2 };console.log(combinedObj); // 输出:{ name: 'Alice', age: 25, location: 'New York' }*///这两行的效果一样,展开之后又组装成对象console.log(state)console.log({...state})console.log(state.resources)console.log(...state.resources)console.log({...state.resources})const newResources = [...state.resources, action.payload];ctx.setState({...state,resources: newResources});}//动作:选择资源@Action(SelectResource)selectResource(ctx: StateContext<ResourceStateModel>, action: SelectResource): void {console.log(`In selectResource, action as below:`)console.log(action)const state = ctx.getState();ctx.setState({...state,selectedResourceId: action.payload});}public ngxsAfterBootstrap(ctx: StateContext<ResourceStateModel>): void {console.log('check bootstrap')//ctx.patchState({resources: [{id: 1, name: "test",}]})}
}
<!-- resource.component.html 文件的内容 -->
<ul><!-- @State支持@Selector()读取状态值、@Action()更新状态值,这些功能普通Service也能实现@State最核心的Angular 提供的 AsyncPipe 是管理 Observable 最简单和推荐的方式。它会自动订阅和取消订阅,无需手动管理。如果手动对普通Service实现订阅和取消订阅还是比较复杂的--><li *ngFor="let resource of resources$ | async" (click)="onSelectResource(resource.id)">{{resource.name}}<span *ngIf="(selectedResource$ | async)?.id === resource.id">(selected)</span></li> 
</ul>
<button (click)="addResource()">Add Resource</button> 
//resource.component.ts 文件的内容
import { Component, inject, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AddResource, SelectResource } from '../../models/resource.actions';
import { Resource } from '../../models/resource.model';
import { Observable } from 'rxjs';
import { ResourceState } from '../../models/resource.state';
import { Store } from '@ngxs/store';@Component({selector: 'app-resource',standalone: true,imports: [ CommonModule ],templateUrl: './resource.component.html',styleUrls: ['./resource.component.less']
})
export class ResourceComponent implements OnInit {//使用 @Select 装饰器获取状态的选择器值public readonly resources$: Observable<Resource[]> = inject(Store).select(ResourceState.getAllResources);public readonly selectedResource$: Observable<Resource | undefined>= inject(Store).select(ResourceState.getSelectedResource);constructor(private store: Store) { }ngOnInit() {}ngAfterViewInit(): void {//Called after ngAfterContentInit when the component's view has been initialized. Applies to components only.//Add 'implements AfterViewInit' to the class.}addResource(): void {const newResource: Resource = {id: Math.floor(Math.random() * 1000), //随机生成Idname: `Resource ${Math.floor(Math.random() * 100)}`};//触发 AddResource 动作,找到resource.state.ts中的@Action(AddResource),向状态中添加新资源this.store.dispatch(new AddResource(newResource));}onSelectResource(resourceId: number): void {//触发 SelectResource 动作,找到resource.state.ts中的@Action(SelectResource)更新选中的资源Idthis.store.dispatch(new SelectResource(resourceId));}
}

四、总结

讨论了这么多,文章中列举出的这些不同后缀名的文件,只有 .d.ts 文件的后缀名不能改,其他后缀名改完之后项目仍然能正常运行,但是 Angular 的这一设定跟 .Net 中的 Controller 类似,约定大于配置,所有的开发者都按照相同的规则进行开发,这样当你读其他人的代码时就能快速入手,确实对大家都是好事。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/92569.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/92569.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

进程状态+进程优先级+进程上下文切换解读

一、进程状态 什么是进程状态&#xff1f;进程状态指的是在操作系统中进程在生命周期中所处的不同阶段。进程状态有哪些呢&#xff1f;我们可以看到上述图片 进程状态分为&#xff1a;创建状态、就绪状态、运行状态、阻塞状态和终止状态所有的操作系统在实现进程状态变化的时候…

Android 原生与 Flutter 通信完整实现 (Kotlin 版)

1. 项目配置 pubspec.yaml 添加依赖 dependencies:flutter:sdk: flutterprovider: ^6.0.52. Flutter 端实现 状态管理类 // settings_provider.dart import package:flutter/foundation.dart;class SettingsProvider with ChangeNotifier {String _themeColor blue;bool _dark…

数字图像处理3

图像线性滤波——目的就是滤去噪声&#xff0c;但是边缘会模糊&#xff0c;整体也模糊线性&#xff1a;邻域平均法&#xff08;4邻域平均和8邻域平均&#xff09;用当前运算点所在邻域的平均值来代替该点的平均值im_for_read"D:\AAAproject\PYproject\EXPERuse\zaosheng.j…

Linux发行版分类与Centos替代品

让centos7气的不轻&#xff0c;这玩意儿太老了&#xff0c;什么都不好配置。 目录Linux 发行版的大致分类1. Red Hat 系列&#xff08;RPM 系&#xff09;2. Debian 系列&#xff08;DEB 系&#xff09;3. Arch 系列4. SUSE 系列CentOS 7 的替代品推荐AlmaLinux 和 Rocky Linux…

大语言模型提示工程与应用:大语言模型对抗性提示安全防御指南

对抗性提示工程 学习目标 理解大语言模型中对抗性提示的风险与防御机制&#xff0c;掌握提示注入、提示泄露和越狱攻击的检测方法&#xff0c;培养安全防护意识。 相关知识点 对抗性攻击类型防御技术 学习内容 1 对抗性攻击类型 1.1 提示注入 提示注入旨在通过使用巧妙…

避不开的数据拷贝(2)

接着上周未完的话题 避不开的数据拷贝。 既然处理器是通用机器&#xff0c;就没有专属数据&#xff0c;所以数据都要从别处调来&#xff0c;这就涉及到了数据搬运&#xff0c;就有了外设的概念。由于不同外设和处理器一起共享数据存储&#xff0c;时间会花在两方面&#xff1a…

娃哈哈经销商“大洗牌”:砍掉年销300万以下经销商

文 | 大力财经据第一财经报道&#xff0c;娃哈哈在宗馥莉“铁腕”策略推动下&#xff0c;正经历经销商体系的重大变革&#xff0c;陆续砍掉年销低于300万元的经销商&#xff0c;方式有时颇为激进&#xff0c;“一刀切”的做法引发诸多争议&#xff0c;部分经销商反馈存在款项未…

drippingblues靶机通关练习笔记

前言 将靶机导入到vmware虚拟机上 靶机下载地址&#xff1a;https://download.vulnhub.com/drippingblues/drippingblues.ova 将网段都设置为nat 信息收集 ip端口扫描 netdiscover -r 192.168.25.1/24 --确定ip nmap -A -p- 192.168.25. kalid的ip&#xff1a;1…

QT第三讲- 机制、宏、类库模块

文章目录 🧩 一、Qt核心机制与类库 🔧 1. 元对象系统(Meta-Object System) ⚡ 2. 信号与槽(Signals & Slots) • 通信机制 📦 3. 属性系统(Property System) 动态属性 例程 类的附加信息 Q_CLASSINFO 例程 🌐 二、全局定义与容器 📝 1. 全局数据类型与函数…

(LeetCode 每日一题) 869. 重新排序得到 2 的幂 (哈希表+枚举)

题目&#xff1a;869. 重新排序得到 2 的幂 思路&#xff1a;哈希表枚举。先预处理出所有的2的幂数&#xff0c;用哈希表来存储。 C版本&#xff1a; class Solution { public:// 哈希表存储所有 2的幂数 按升序排列的形式unordered_set<string> st;// 预处理出所有的2…

WebAssembly技术详解:从浏览器到云原生的高性能革命

引言&#xff1a;WebAssembly的诞生与使命 2015年&#xff0c;当Mozilla、Google、Microsoft和Apple四大浏览器厂商联合发布WebAssembly&#xff08;Wasm&#xff09;技术预览时&#xff0c;业界尚未意识到这将开启Web性能的新纪元。作为继HTML、CSS、JavaScript之后的第四种We…

性能解析案例

异步io是内核fd与应用程序直接的关系io 多路复用1.检测io是否就绪2.read/write消息队列kafka&#xff1a;1.典型应用 &#xff1a;异步处理&#xff0c;系统解耦&#xff0c;流量削峰&#xff0c;日志处理2.核心原理&#xff1a;kafka体系结构以及读写流程3.具体操作&#xff1…

青龙峡拔韭菜

我们一年四季&#xff0c;除了冬天不往山里进&#xff0c;其余季节&#xff0c;只要天气允许&#xff0c;我们都会进山。在山里拔韭菜&#xff0c;是我们百做不烦的一件事。今年大旱&#xff0c;从五月份上山找韭菜&#xff0c;没有如愿。直到入秋后&#xff0c;我们再次去青龙…

5、docker镜像管理命令

1、命令总览命令&#xff08;含关键参数&#xff09;作用出现频率备注docker buildx build --platform … -t … --push .一次构建并推送多平台镜像高频需先 docker buildx create --usedocker buildx build -o typedocker,destxxx.tar .构建后离线导出 tar 包中频只导出单平台…

阿里云ECS云服务器临时升级带宽方法

阿里云ECS云服务器临时升级带宽方法一、背景与需求二、原理三、操作步骤步骤 0: 准备工作步骤 1: 创建弹性网卡 (ENI)步骤 2: 创建并绑定弹性公网IP (EIP)步骤 3: SSH登录ECS并切换到高速通道 (eth1)步骤 4: 执行你的高带宽任务步骤 5: 任务完成&#xff0c;切回默认网卡 (eth0…

Java语言简介

一.Java语言的起源 Java语言的前身是Oka语言,是美国Sun Microsystems公司于1991年推出的,仅限于公司内部使用的语言。1995年,Sun公司将Oak语言更名为Java语言,并正式向公众推出。这之后,Java语言不断更新,其类库越来越丰富,性能逐步提升,应用领域也显著拓展,已成为当今…

VUE+SPRINGBOOT从0-1打造前后端-前后台系统-视频列表与视频播放

在现代Web开发中&#xff0c;视频播放功能已成为许多网站的基本需求。本文将基于Vue.js框架&#xff0c;详细讲解如何实现一个视频列表与播放器交互的功能模块。这个组件可以让用户点击列表中的视频项来播放对应的视频&#xff0c;并支持再次点击关闭播放器。功能概述我们实现的…

详解 Seaborn:让数据可视化更简单高效的 Python 库

在数据科学领域&#xff0c;可视化是理解数据、挖掘规律的重要手段。今天要为大家介绍的 Seaborn 库&#xff0c;正是数据可视化领域的一把 “利器”。它基于 Matplotlib 开发&#xff0c;却凭借更简洁的接口和更美观的默认样式&#xff0c;成为众多数据分析师的首选工具。下面…

Cesium1.95中如何高效管理 1500 个高频实体

一、建议&#xff1a;不要频繁创建/销毁&#xff0c;而是复用对象&#xff1b;​​​​使用 CallbackProperty更新位置而不是删了重建&#xff1b;​​​​对大量 Billboard / Polyline / Label&#xff0c;优先使用对应的 *Collection&#xff0c;然后批量更新&#xff1b;​​…

全面了解机器语言之kmeans

深入理解 KMeans 聚类算法&#xff1a;原理、实现与应用在机器学习领域&#xff0c;聚类算法作为无监督学习的核心技术之一&#xff0c;一直以来都是数据挖掘和模式识别的重要工具。其中&#xff0c;KMeans 算法以其简洁的原理、高效的计算性能和广泛的适用性&#xff0c;成为最…