TypeScript: 模块优于命名空间

65 阅读3分钟

在开发大型 TypeScript 应用程序时,开发人员往往会面临一个选择,那就是是否使用命名空间或模块。TypeScript 提供了两种方式来组织代码,命名空间(早期称为“内部模块”)和模块(早期称为“外部模块”)。这两种方式都有其用途,但在大多数情况下,更推荐使用模块,下面就来说说为什么更推荐用模块而非用命名空间。

模块是标准化的

JavaScript 社区已经达成了关于模块的共识,那就是 ES6 标准的 importexport 语法。这种标准在所有的现代 JavaScript 运行环境中都是原生支持的,包括现代浏览器和 Node.js。TypeScript 的模块语法就是遵循这个标准的,这意味着 TypeScript 的模块系统可以无缝地与 JavaScript 的生态系统集成。

相反,TypeScript 的命名空间是 TypeScript 特有的功能,需要被编译为较长的 JavaScript IIFE(立即执行函数表达式)结构以便在浏览器中使用。这种转换在阅读和调试编译后的 JavaScript 代码时可能会带来额外的复杂性。

模块支持异步和延迟加载

由于模块是 JavaScript 的原生特性,它们可以被浏览器或 Node.js 在运行时异步加载。这种特性可以让你的应用在启动时只加载所需的模块,而非全部模块,从而减少应用的初始加载时间,提高性能。这在大型应用中尤其有用,因为你可能有很多模块只在特定条件下才需要。

相比之下,命名空间并不支持异步或延迟加载。使用命名空间时,所有的代码都需要在应用启动时一次性加载,这可能会导致初始加载时间过长。

模块有明确的依赖关系

模块的 importexport 语法可以让你清楚地看到模块之间的依赖关系。通过查看模块顶部的 import 语句,你可以快速找出这个模块依赖了哪些其他模块。这种明确的依赖关系使得代码更容易理解和维护。

与此相反,命名空间并不能提供这样的特性。在使用命名空间时,不同的文件可能会随意地添加或修改全局的命名空间,这可能会导致难以理解的依赖关系和难以预见的副作用。

明确指明依赖的好处很多,可以提升代码可读性,可以强制模块隔离(命名空间会自动合并,而模块不会),还可以做静态分析。不要小看这点一点,在大型前端项目中,剔除无用代码,把编译得到的代码分拆到多个文件中,对性能的提升是至关重要的。

如果在NodeJS环境中运行TypeScript程序,模块也是更明智的选择,因为NodeJS内置对CommonJS的支持。在浏览器环境中,有些开发人员为了方便而喜欢使用命名空间,但在大型项目中,建议全部使用模块,而非命名空间,但通常在通过 d.ts 文件标记 js 库类型的时候使用命名空间,主要作用是给编译器编写代码的时候参考使用。