模块与命名空间:组织大型打字稿项目的正确方法是什么?
Modules vs. Namespaces: What is the correct way to organize a large typescript project?
我是打字稿的新手,我正在为 WebGl 编写一个小型原型框架。我目前正在重构我的项目,运行 遇到了一些问题,例如如何组织我的项目,因为这两种(模块和命名空间)方法似乎都有严重的缺点。
本文post不是关于如何使用这些模式,而是如何克服每个模式带来的问题。
现状:使用命名空间
来自 C#,这似乎是最自然的方式。每个 class/module 都有合适的命名空间,我在 tsconfig.json 中提供了 "outFile" 参数,因此所有内容都连接到一个大文件中。
编译后,我将根名称空间作为全局对象。项目中没有内置依赖项,因此您必须手动提供所需的 *.js 文件 html(不好)
示例文件
namespace Cross.Eye {
export class SpriteFont {
//code omitted
}
}
示例用法(在html中提供js文件之前必须确保Cross命名空间被加载到全局命名空间)
namespace Examples {
export class _01_BasicQuad {
context: Cross.Eye.Context;
shader: Cross.Eye.ShaderProgram;
//code omitted
}
}
优点
- 如果您来自 C#/Java
,则可以直接使用
- 独立于文件名 - 重命名文件不会破坏您的代码。
- 易于重构:IDEs 可以轻松重命名 namespaces/classes,并且更改将在整个代码中一致应用。
- 方便:向项目添加 class 就像添加文件并在所需命名空间中声明它一样简单。
缺点
For most projects we recommend using external modules and using namespace for quick demos and porting old JavaScript code.
来自 https://basarat.gitbooks.io/typescript/content/docs/project/namespaces.html
- 根命名空间总是(?)一个全局对象(不好)
- 不能(?)与 browserify 或 webpack 等工具一起使用,这对于将 lib 与其依赖项捆绑在一起或在实际使用时将您的自定义代码与 lib 捆绑在一起至关重要。
- 如果你打算发布一个 npm 模块,这是不好的做法
最先进的 (?):模块
Typescript 支持 ES6 模块,它们是新的和闪亮的,每个人似乎都同意它们是要走的路。这个想法似乎是每个文件都是一个模块,通过在 import 语句中提供文件,您可以非常明确地定义您的依赖项,这使得捆绑工具可以轻松有效地打包您的代码。我大多数情况下每个文件都有一个 class,这似乎不适用于 dhte 模块模式。
这是我重构后的文件结构:
此外,我在每个文件夹中都有一个 index.ts 文件,因此我可以通过 import * as FolderModule from "./folder"
导入其所有 classes
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
}
用法示例。如您所见,我不再需要遍历名称空间,因为我可以直接导入 classes。
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 等捆绑工具,它们也可以捆绑您所有的依赖项
- 导入 classes 时更加灵活,不再需要遍历命名空间链。
缺点
- 如果每个 class 都是不同的文件,那将非常乏味,您将不得不一遍又一遍地键入相同的导入语句。
- 重命名文件会破坏您的代码(不好)。
- 重构 class 名称不会传播到您的导入(非常糟糕 - 可能取决于您的 IDE 不过,我使用的是 vs-code)
IMO 这两种方法似乎都有缺陷。命名空间似乎非常过时,对于大型项目不切实际并且与常用工具不兼容,同时使用模块非常不方便并且破坏了我最初为 typescript 改编的一些功能。
在一个完美的世界中,我会使用命名空间模式编写我的框架并将其导出为一个模块,然后可以将其导入并与其依赖项捆绑在一起。然而,如果没有一些丑陋的技巧,这似乎是不可能的。
所以这是我的问题:你是如何处理这些问题的?我怎样才能最大限度地减少每种方法所带来的缺点?
更新
在对打字稿和 javascript 总体开发有了更多的经验之后,我必须指出模块可能是所有用例中 90% 的方式。
希望最后 10% 是使用全局名称空间的遗留项目,您想用一些打字稿(顺便说一下,它工作得很好)来增添趣味。
我对模块的许多批评可以(并且已经)通过更好的 IDE 支持得到解决。 Visual Studio 代码添加了自动模块解析,效果很好。
tl;dr:不要选择过去。选择未来:模块。
在 ES6 模块规范的早期草案中,有一个 内联模块 概念,然后 has been eliminated in September 2013. However, this notion was already implemented by the TypeScript team, in 2012, with the first beta versions of the language: it was internal modules. Then, the final specification for ES6 modules has been released in July 2014 without inline modules. A year later, in July 2015, with the version 1.5 of TypeScript, internal modules has been renamed 到 命名空间 为了避免与标准混淆。
命名空间是一项遗留功能。它不会成为 ECMAScript 语言的一部分。并且 TypeScript 团队将继续遵循该标准。自 2014 年 7 月发布 ECMAScript 模块标准以来,关于 TS 命名空间的内容没有任何改进。
Cons [of ES6 modules]
- Very tedious if every class is a different file, you will have to type the same import statements over and over.
- Renaming files will break your code (bad).
- Refactoring class names won't propagate through to your imports (very bad - might depend on your IDE though, I'm using vs-code)
我们希望未来的 IDE 在这些问题上有所改进。第一个已经被WebStorm解决了。
我是打字稿的新手,我正在为 WebGl 编写一个小型原型框架。我目前正在重构我的项目,运行 遇到了一些问题,例如如何组织我的项目,因为这两种(模块和命名空间)方法似乎都有严重的缺点。
本文post不是关于如何使用这些模式,而是如何克服每个模式带来的问题。
现状:使用命名空间
来自 C#,这似乎是最自然的方式。每个 class/module 都有合适的命名空间,我在 tsconfig.json 中提供了 "outFile" 参数,因此所有内容都连接到一个大文件中。 编译后,我将根名称空间作为全局对象。项目中没有内置依赖项,因此您必须手动提供所需的 *.js 文件 html(不好)
示例文件
namespace Cross.Eye {
export class SpriteFont {
//code omitted
}
}
示例用法(在html中提供js文件之前必须确保Cross命名空间被加载到全局命名空间)
namespace Examples {
export class _01_BasicQuad {
context: Cross.Eye.Context;
shader: Cross.Eye.ShaderProgram;
//code omitted
}
}
优点
- 如果您来自 C#/Java ,则可以直接使用
- 独立于文件名 - 重命名文件不会破坏您的代码。
- 易于重构:IDEs 可以轻松重命名 namespaces/classes,并且更改将在整个代码中一致应用。
- 方便:向项目添加 class 就像添加文件并在所需命名空间中声明它一样简单。
缺点
For most projects we recommend using external modules and using namespace for quick demos and porting old JavaScript code.
来自 https://basarat.gitbooks.io/typescript/content/docs/project/namespaces.html
- 根命名空间总是(?)一个全局对象(不好)
- 不能(?)与 browserify 或 webpack 等工具一起使用,这对于将 lib 与其依赖项捆绑在一起或在实际使用时将您的自定义代码与 lib 捆绑在一起至关重要。
- 如果你打算发布一个 npm 模块,这是不好的做法
最先进的 (?):模块
Typescript 支持 ES6 模块,它们是新的和闪亮的,每个人似乎都同意它们是要走的路。这个想法似乎是每个文件都是一个模块,通过在 import 语句中提供文件,您可以非常明确地定义您的依赖项,这使得捆绑工具可以轻松有效地打包您的代码。我大多数情况下每个文件都有一个 class,这似乎不适用于 dhte 模块模式。
这是我重构后的文件结构:
此外,我在每个文件夹中都有一个 index.ts 文件,因此我可以通过 import * as FolderModule from "./folder"
导入其所有 classes
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
}
用法示例。如您所见,我不再需要遍历名称空间,因为我可以直接导入 classes。
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 等捆绑工具,它们也可以捆绑您所有的依赖项
- 导入 classes 时更加灵活,不再需要遍历命名空间链。
缺点
- 如果每个 class 都是不同的文件,那将非常乏味,您将不得不一遍又一遍地键入相同的导入语句。
- 重命名文件会破坏您的代码(不好)。
- 重构 class 名称不会传播到您的导入(非常糟糕 - 可能取决于您的 IDE 不过,我使用的是 vs-code)
IMO 这两种方法似乎都有缺陷。命名空间似乎非常过时,对于大型项目不切实际并且与常用工具不兼容,同时使用模块非常不方便并且破坏了我最初为 typescript 改编的一些功能。
在一个完美的世界中,我会使用命名空间模式编写我的框架并将其导出为一个模块,然后可以将其导入并与其依赖项捆绑在一起。然而,如果没有一些丑陋的技巧,这似乎是不可能的。
所以这是我的问题:你是如何处理这些问题的?我怎样才能最大限度地减少每种方法所带来的缺点?
更新
在对打字稿和 javascript 总体开发有了更多的经验之后,我必须指出模块可能是所有用例中 90% 的方式。
希望最后 10% 是使用全局名称空间的遗留项目,您想用一些打字稿(顺便说一下,它工作得很好)来增添趣味。
我对模块的许多批评可以(并且已经)通过更好的 IDE 支持得到解决。 Visual Studio 代码添加了自动模块解析,效果很好。
tl;dr:不要选择过去。选择未来:模块。
在 ES6 模块规范的早期草案中,有一个 内联模块 概念,然后 has been eliminated in September 2013. However, this notion was already implemented by the TypeScript team, in 2012, with the first beta versions of the language: it was internal modules. Then, the final specification for ES6 modules has been released in July 2014 without inline modules. A year later, in July 2015, with the version 1.5 of TypeScript, internal modules has been renamed 到 命名空间 为了避免与标准混淆。
命名空间是一项遗留功能。它不会成为 ECMAScript 语言的一部分。并且 TypeScript 团队将继续遵循该标准。自 2014 年 7 月发布 ECMAScript 模块标准以来,关于 TS 命名空间的内容没有任何改进。
Cons [of ES6 modules]
- Very tedious if every class is a different file, you will have to type the same import statements over and over.
- Renaming files will break your code (bad).
- Refactoring class names won't propagate through to your imports (very bad - might depend on your IDE though, I'm using vs-code)
我们希望未来的 IDE 在这些问题上有所改进。第一个已经被WebStorm解决了。