模块与命名空间:组织大型打字稿项目的正确方法是什么?

mod*_*777 27 namespaces module browserify typescript webpack

我是打字稿的新手,我正在为WebGl编写一个小型的原型框架.我正在重构我的项目,并遇到了一些问题,如何组织我的项目,因为(模块和命名空间)方法似乎都有严重的缺点.

这篇文章不是关于如何使用这些模式,而是如何克服这些模式带来的问题.

现状:使用名称空间

来自C#,这似乎是最自然的方式.每个类/模块都获得它适当的命名空间,我在tsconfig.json中提供"outFile"参数,因此所有内容都连接成一个大文件.编译后,我将根命名空间作为全局对象.依赖项没有内置到项目中,所以你手动必须在你的html中提供所需的*.js文件(不好)

示例文件

namespace Cross.Eye {
    export class SpriteFont {   
        //code omitted
    }    
}
Run Code Online (Sandbox Code Playgroud)

示例用法(通过在html中提供js文件,您必须确保将Cross命名空间加载到全局命名空间中)

namespace Examples {
    export class _01_BasicQuad {
        context: Cross.Eye.Context;
        shader: Cross.Eye.ShaderProgram;

        //code omitted
    }
}
Run Code Online (Sandbox Code Playgroud)

优点

  • 如果你是从C#/ Java出来的话,可以直截了当地使用
  • 独立的文件名 - 重命名文件不会破坏您的代码.
  • 易于重构:IDE可以轻松地重命名命名空间/类,并且将通过代码一致地应用更改.
  • 方便:向项目添加类就像添加文件并在所需的命名空间中声明它一样简单.

缺点

对于大多数项目,我们建议使用外部模块并使用命名空间进行快速演示和移植旧的JavaScript代码.

来自https://basarat.gitbooks.io/typescript/content/docs/project/namespaces.html

  • 根命名空间始终是(?)一个全局对象(坏)
  • 不能(?)与browserify或webpack等工具一起使用,这对于将lib与其依赖项捆绑在一起是必不可少的,或者在实际使用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";
Run Code Online (Sandbox Code Playgroud)

示例文件 - 我认为问题在这里变得清晰可见..

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
}
Run Code Online (Sandbox Code Playgroud)

用法示例.如您所见,我不再需要遍历命名空间,因为我可以直接导入类.

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.
}
Run Code Online (Sandbox Code Playgroud)

优点

  • 使代码更加模块化,因为它不再绑定到名称空间.
  • 您可以使用browserfy或webpack等捆绑工具,它们也可以捆绑所有依赖项
  • 导入类时更灵活,不再需要遍历命名空间链.

缺点

  • 如果每个类都是不同的文件,则非常繁琐,您必须反复键入相同的import语句.
  • 重命名文件将破坏您的代码(坏).
  • 重构类名称不会传播到您的导入(非常糟糕 - 可能取决于您的IDE,我使用的是vs-code)

IMO这两种方法似乎都存在缺陷.命名空间似乎非常过时,对于大型项目来说是不切实际的,并且与使用模块的常用工具不兼容,因此使用模块非常不方便,并且首先打破了我正在调整打字稿的一些功能.

在一个完美的世界中,我会使用命名空间模式编写我的框架并将其导出为一个模块,然后可以将其导入并与其依赖项捆绑在一起.然而,如果没有一些丑陋的黑客攻击,这似乎是不可能的.

所以这是我的问题:你是如何处理这些问题的?如何最大限度地减少每种方法所暗示的缺点?

更新

在获得一些关于打字稿和javascript开发的经验之后,我必须指出模块可能是90%的所有用例的方法.

最后10%是希望使用全局命名空间的遗留项目,你想要用一些小脚本(这顺便说一下)很有趣.

我对模块的大部分评论都可以通过更好的IDE支持来解决.Visual Studio Code自此添加了自动模块分辨率,效果很好.

Pal*_*leo 10

tl;博士:不要选择过去.选择未来:模块.

在ES6模块规范的早期草案中,有一个内联模块概念,然后在2013年9月被淘汰.但是,2012年,TypeScript团队已经实现了这一概念,该语言的第一个beta版本:它是内部模块.然后,ES6模块的最终规范已于2014年7月发布,没有内联模块.一年后,在2015年7月,使用TypeScript 1.5版,内部模块 已重命名命名空间,以避免与标准混淆.

命名空间是一项遗留功能.它不会是ECMAScript语言的一部分.TypeScript团队将继续遵循该标准.自2014年7月发布ECMAScript模块标准以来,TS名称空间没有任何改进.

缺点[ES6模块]

  • 如果每个类都是不同的文件,则非常繁琐,您必须反复键入相同的import语句.
  • 重命名文件将破坏您的代码(坏).
  • 重构类名称不会传播到您的导入(非常糟糕 - 可能取决于您的IDE,我使用的是vs-code)

我们希望未来的IDE能够对这些问题进行一些改进.第一个已经被WebStorm解决了.

  • @EJMason什么......一个典型的项目在gitignore中有node_modules.在众多原因中,我看不出这是一个好主意. (4认同)