模块与命名空间:组织大型打字稿项目的正确方法是啥?
Posted
技术标签:
【中文标题】模块与命名空间:组织大型打字稿项目的正确方法是啥?【英文标题】:Modules vs. Namespaces: What is the correct way to organize a large typescript project?模块与命名空间:组织大型打字稿项目的正确方法是什么? 【发布时间】:2017-02-11 03:25:41 【问题描述】:我对 typescript 很陌生,我正在为 WebGl 编写一个小型原型框架。我目前正在重构我的项目,并且在如何组织我的项目方面遇到了一些问题,因为(模块和命名空间)方法似乎都有严重的缺陷。
这篇文章不是关于如何使用这些模式,而是如何克服这些模式带来的问题。
现状:使用命名空间
来自 C#,这似乎是最自然的方式。每个类/模块都有适当的命名空间,我在 tsconfig.json 中提供“outFile”参数,因此所有内容都连接到一个大文件中。 编译后,我将根命名空间作为全局对象。依赖项没有内置到项目中,因此您必须手动在 html 中提供所需的 *.js 文件(不好)
示例文件
namespace Cross.Eye
export class SpriteFont
//code omitted
使用示例(您必须先确保将 Cross 命名空间加载到全局命名空间中,方法是在 html 中提供 js 文件)
namespace Examples
export class _01_BasicQuad
context: Cross.Eye.Context;
shader: Cross.Eye.ShaderProgram;
//code omitted
优点
如果您来自 C#/Java,则可以直接使用 独立于文件名 - 重命名文件不会破坏您的代码。 易于重构:IDE 可以轻松地重命名命名空间/类,并且更改将在整个代码中一致地应用。 方便:向项目中添加类就像添加文件并在所需的命名空间中声明一样简单。缺点
对于大多数项目,我们建议使用外部模块并使用命名空间进行快速演示和移植旧的 javascript 代码。
来自https://basarat.gitbooks.io/typescript/content/docs/project/namespaces.html
根命名空间总是(?)一个全局对象(坏) 不能(?)与 browserify 或 webpack 等工具一起使用,这对于将 lib 与其依赖项捆绑或在实际使用时将您的自定义代码与 lib 捆绑至关重要。 如果您计划发布 npm 模块,这是一种不好的做法最先进的 (?):模块
Typescript 支持 ES6 模块,它们是新的和闪亮的,每个人似乎都同意它们是要走的路。这个想法似乎是每个文件都是一个模块,通过在 import 语句中提供文件,您可以非常明确地定义您的依赖项,这使得捆绑工具可以轻松有效地打包您的代码。我通常每个文件都有一个类,这似乎不适用于 dhte 模块模式。
这是我重构后的文件结构:
此外,我在每个文件夹中都有一个 index.ts 文件,因此我可以通过 import * as FolderModule from "./folder"
导入其所有类
export * from "./AggregateLoader";
export * from "./ImageLoader";
export * from "./TiledLoader";
export * from "./XhrLoaders";
export * from "./XmlSpriteFontLoader";
示例文件 - 我认为问题在这里变得清晰可见..
import SpriteFont from "./SpriteFont";
import ISpriteTextGlyph, ISpriteChar from "./Interfaces";
import Event,EventArgs from "../../Core";
import Attribute, AttributeConfiguration from "../Attributes";
import DataType from "../GlEnums";
import VertexStore from "../VertexStore";
import IRectangle from "../Geometry";
import vec3 from "gl-matrix";
export class SpriteText
// code omitted
示例用法。如您所见,我不再需要遍历命名空间,因为我可以直接导入类。
import
Context,
Shader,
ShaderProgram,
Attribute,
AttributeConfiguration,
VertexStore,
ShaderType,
VertexBuffer,
PrimitiveType
from "../cross/src/Eye";
import
Assets,
TextLoader
from "../cross/src/Load";
export class _01_BasicQuad
context: Context;
shader: ShaderProgram;
// code omitted.
优点
使您的代码更加模块化,因为它不再绑定到命名空间。 您可以使用 browserfy 或 webpack 等捆绑工具,它们也可以捆绑您的所有依赖项 您在导入类时更加灵活,不再需要遍历命名空间链。缺点
如果每个类都是不同的文件,您将不得不一遍又一遍地键入相同的导入语句,这非常繁琐。 重命名文件会破坏您的代码(不好)。 重构类名不会传播到您的导入中(非常糟糕 - 虽然可能取决于您的 IDE,但我使用的是 vs-code)IMO 两种方法似乎都有缺陷。命名空间似乎非常过时,对于大型项目不切实际,并且在使用模块时与常用工具不兼容,非常不方便,并且破坏了我最初调整 typescript 的一些功能。
在一个完美的世界中,我会使用命名空间模式编写我的框架并将其导出为一个模块,然后可以将其导入并与其依赖项捆绑在一起。但是,如果没有一些丑陋的 hack,这似乎是不可能的。
所以这是我的问题:您是如何处理这些问题的?我怎样才能最大限度地减少每种方法所隐含的缺点?
更新
在获得了更多关于 typescript 和 javascript 开发的一般经验后,我不得不指出,模块可能是 90% 的用例。
最后 10% 希望是使用全局命名空间的遗留项目,你想用一点打字稿来增加趣味(顺便说一句,效果很好)。
我对模块的许多批评可以(并且已经)通过更好的 IDE 支持来解决。 Visual Studio Code 已经添加了自动模块解析,效果很好。
【问题讨论】:
【参考方案1】:tl;dr:不要选择过去。选择未来:模块。
在 ES6 模块规范的早期草案中,有一个 内联模块 的概念,然后是 has been eliminated in September 2013。然而,TypeScript 团队已经在 2012 年实现了这个概念,该语言的第一个 beta 版本:它是内部模块。然后,没有内联模块的 ES6 模块 has been released in July 2014 的最终规范。一年后,也就是 2015 年 7 月,TypeScript 1.5 版,内部模块 has been renamed 到 namespaces 以避免与标准混淆。
命名空间是一项遗留功能。它不会成为 ECMAScript 语言的一部分。 TypeScript 团队将继续遵循该标准。自 2014 年 7 月 ECMAScript 模块标准发布以来,TS 命名空间没有任何改进。
[ES6 模块的]缺点
如果每个类都是不同的文件,您将不得不一遍又一遍地键入相同的导入语句,这非常繁琐。 重命名文件会破坏您的代码(不好)。 重构类名不会传播到您的导入中(非常糟糕 - 不过可能取决于您的 IDE,我使用的是 vs-code)
我们希望未来的 IDE 能够对这些问题进行一些改进。第一个已经被 WebStorm 解决了。
【讨论】:
这是一篇古老的帖子,但可能值得做的是将经常使用的模块复制到 node_modules 的文件夹中。然后你可以在没有路径的情况下导入。 @EJMason 什么......一个典型的项目在 gitignore 中有 node_modules。在众多原因中,我看不出这是一个好主意。以上是关于模块与命名空间:组织大型打字稿项目的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
使用打字稿在 Firebase Cloud 函数中动态导入类的正确方法是啥?