【发布时间】: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 已经添加了自动模块解析,效果很好。
【问题讨论】:
标签: typescript module namespaces webpack browserify