Angular 的基本 Typescript:理解模块
介绍
您开始学习 Angular,但很难知道什么是 Angular,什么是 Typescript。
本指南帮助您了解 Typescript 模块的工作原理以及它们在 Angular 中的使用方式。我看到的 Angular CLI 生成的导入和导出语句是什么?如何创建自己的模块?
为了回答这些问题并展示我们需要学习的概念,我们将使用在应用程序内处理购物车功能的场景。
什么是 Typescript 模块?
模块化代码的目标是每个单独的模块都提供一项功能,并通过定义良好的接口进行公开。模块工作方式的内部细节是隔离的,这使得测试和重构更加容易。
模块可以通过导入来使用其他模块。反过来,模块只导出它希望其他代码能够使用的内容。
在 Typescript 中,模块只是一个导入或导出某些内容的文件。
为了使应用程序运行,上述模块之间的依赖关系通过模块加载器解决。在模块 B加载之前,浏览器无法加载模块 A的代码。
Typescript 不提供运行时,它只是一个 javascript 的转换器。Angular CLI 会为您处理模块加载方面的问题。当它构建您的 Angular 应用时,它使用 WebPack 作为其使用的加载器技术。
Angular 模块与 Typescript 模块
Angular 也有模块的概念,您将在代码中看到它作为@Module()定义。这些模块独立于 Typescript 模块。
Angular 模块遵循相同的模块化软件概念,但级别不同。它们旨在将应用程序划分为功能区域,并配有 UI 和服务定义。
创建模块
创建模块就像创建具有导入或导出语句的 Typescript 文件一样简单。
模块可以导出一个或多个声明:类、函数、接口、枚举、常量或类型别名。
对于本指南的场景,我们将研究电子商务应用程序所需的ProductsService和相关类型。
// app/shopping-cart/products.service.ts
export class ProductsService {
// .. Service code here
}
export interface Product {
// Interface declarations
}
// Private function to this module, not on the global namespace
function logDebug(message: string) { console.log(message); }
从上面的products.service.ts Typescript 模块中,仅导出ProductsService类和Product接口。它们是此模块的任何导入器可用的唯一类型。
logDebug函数是私有的,仅供本模块内使用。
对于非模块化的 javascript,logDebug函数将被放置在全局命名空间中。如果其他加载的 javascript 覆盖或以其他方式更改此函数,则可能导致意外后果。
回想一下,所有 Typescript 模块都是独立的,因此在自己的范围内运行,而不是在全局范围内。logDebug函数仅在此模块内可用。另一个模块可以安全地声明自己的名为 logDebug 的函数,并且它不会与此模块冲突。
出口方式
有几种方法可以从模块导出声明。
正如我们已经看到的,在 Angular 中典型的做法是,在声明时立即导出:
// Export at time of declaration
export class ProductsService {
// .. Service code here
}
export interface Product {
// Interface declarations
}
或者,您也可以在单个导出语句中导出一个或多个声明:
class ProductsService {
// .. Service code here
}
interface Product {
// Interface declarations
}
// Export as a single statement
export { ProductsService, Product }
该选项保留所有导出,这样做的好处是可以清楚地看到模块导出的公共接口。
使用模块
为了使用我们的模块,我们需要导入它。
假设我们有一个cart.component.ts需要使用 ProductsService 。它可以像这样导入它:
// app/shopping-cart/cart.component.ts
import { ProductsService } from './products.service';
这是从我们的模块products.service.ts导入ProductsService类。
我们可以从模块中导入ProductsService,因为它已被导出。如果我们尝试导入logDebug函数,我们将在编译时收到错误:
// cart.component.ts
//ERROR: logDebug is not exported, so cannot be imported
import { logDebug } from './products.service';
模块分辨率
Typescript 有一个模块解析的概念,它在编译时使用该概念来查找要导入的模块。
在前面的例子中,import语句中对模块的引用是相对路径,因此我们希望products.service.ts是cart.component.ts文件的兄弟。
请注意,不需要.ts扩展名!我们的 Typescript 实际上将被转换为 javascript,因此最终模块具有.js扩展名。
我们也可以导入其他文件扩展名:来自 NPM 包的.tsx或.d.ts 。
这就是模块解析的工作方式。对于我们的 Angular 应用来说,我们不需要担心这个问题,但值得知道的是,这就是import语句中没有文件扩展名的原因。
别名类型
有时我们要使用的两个模块会导出同名的类型。
当我们尝试从另一个模块(比如电子商务 CMS)导入产品时,我们会收到错误:
import { ProductsService, Product } from './products.service';
// ERROR: Duplicate identifier 'Product'
import { Product } from 'ecommerceCMS/products';
为了解决这个问题,我们可以给类型添加别名,以避免命名冲突:
import { ProductsService, Product } from './products.service';
// Now available as CMSProduct
import { Product as CMSProduct} from 'ecommerceCMS/products';
或者,您可以将模块中的所有类型导入到变量中:
import * as products from './products.service';
import { Product } from 'ecommerceCMS/products';
这会将我们模块中的所有类型放入产品变量中。您可以将类型引用为products.ProductsService和products.Product ,避免与电子商务 CMS产品发生命名冲突。
将出口量汇总成桶
随着购物车功能的增长,我们最终会拥有越来越多的模块。对于任何导入器来说,这意味着会有很多导入语句,并且很难维护。
理想情况下,我们希望将所有较小的模块汇总为一个模块,我们称之为shoppingCart。
这是通过barrels实现的。Barrels 是将许多单独的模块拉到一起,重新导出它们的声明,从而创建一个单一的、有凝聚力的模块的模块。
定义桶
为了为我们的购物车定义一个桶,我们在购物车根文件夹中创建一个名为index.ts的文件,该文件只是重新导出我们的其他模块:
// app/shoppingCart/index.ts
export { ProductService, Product } from './shoppingCart/products.service';
export { CartComponent } from './shoppingCart/cart.component'
进口桶
barrel 与其他模块一样,因此我们可以用相同的方式导入它。不过,我们在名为index.ts的文件中定义了 barrel 。
就像 Web 服务器提供默认页面一样,Typescript 模块解
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~