模块与命名空间:组织大型打字稿项目的正确方法是什么? [英] Modules vs. Namespaces: What is the correct way to organize a large typescript project?

查看:104
本文介绍了模块与命名空间:组织大型打字稿项目的正确方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对打字稿很陌生,我正在为WebGl编写一个小型原型框架.我目前正在重构我的项目,并且在如何组织我的项目时遇到了一些问题,因为(模块和名称空间)这两种方法似乎都存在严重的缺陷.

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

状态现状:使用名称空间

来自C#,这似乎是最自然的方法.每个类/模块都获得合适的名称空间,我在tsconfig.json中提供了"outFile"参数,因此所有内容都串联到一个大文件中. 编译后,我将我的根名称空间作为全局对象.依赖关系未内置到项目中,因此您必须手动在html中提供所需的* .js文件(不好)

示例文件

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
  • 文件名的独立性-重命名文件不会破坏您的代码.
  • 易于重构: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";

示例文件-我认为问题在这里变得很明显..

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的两种方法似乎都有缺陷.命名空间似乎已经过时了,对于大型项目来说是不切实际的,并且与通用工具不兼容,而使用模块却非常不便,并且破坏了我最初为打字稿改编的某些功能.

在一个理想的世界中,我将使用名称空间模式编写框架并将其导出为模块,然后可以将其导入并与其依赖项捆绑在一起.但是,如果没有一些难看的骇客,这似乎是不可能的.

所以这是我的问题:您如何处理这些问题?如何使每种方法带来的弊端最小化?

更新

在获得了一般的打字稿和JavaScript开发经验之后,我必须指出,模块可能是90%的所有用例都可以采用的方式.

最后10%是希望使用全局命名空间的遗留项目,您希望使用一些小的打字稿来增添趣味(顺便说一句,效果很好).

我对模块的大多数批评都可以(并且已经)通过更好的IDE支持得以解决.此后,Visual Studio Code添加了自动模块解析功能,效果很好.

解决方案

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

在ES6模块规范的早期草稿中,有一个内联模块概念,然后该概念 不带内联模块.一年后的2015年7月,使用TypeScript 1.5版,内部模块 已重命名命名空间,以避免与标准混淆.

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

[ES6模块的]缺点

  • 如果每个类都是一个不同的文件,那将非常麻烦,您将不得不一遍又一遍地键入相同的导入语句.
  • 重命名文件会破坏您的代码(错误).
  • 重构类名不会传播到您的导入中(非常糟糕-虽然可能取决于您的IDE,但我使用的是vs-code)

我们希望将来的IDE在这些问题上有所改进. WebStorm已经解决了第一个问题.

I'm pretty new to typescript and I'm writing a small prototyping framework for WebGl. I'm currently refactoring my project and have run into some problems as how to organize my project, as both (modules and namespaces) approaches seem to have serious drawbacks.

This post is not about HOW to use these patterns, but how to overcome the problems each of these brings.

Status Quo: Using namespaces

Coming from C# this seemed like the most natural way to go. Every class/module gets it's appropriate namespace and I supply the "outFile" parameter in the tsconfig.json so everything gets concatenated into one large file. After compilation I have my root namespace as a global object. Dependencies are not built into the project, so you manually have to supply the needed *.js files in you html (not good)

Example file

namespace Cross.Eye {
    export class SpriteFont {   
        //code omitted
    }    
}

Example usage (You have to make sure the Cross namespace is loaded into the global namespace before by suppling the js file in the html)

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

        //code omitted
    }
}

Pros

  • Straightforward to use if you are comming from C#/Java
  • Independant of filenames - renaming files won't break your code.
  • Easy to refactor: IDEs can easily rename namespaces/classes and the changes will be applied consistently througout your code.
  • Convenience: Adding a class to the project is as simple as adding a file and declaring it in the desired namespace.

Cons

For most projects we recommend using external modules and using namespace for quick demos and porting old JavaScript code.

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

  • A root namespace is always(?) a global object (bad)
  • Cannot(?) be used with tools like browserify or webpack which is essential to bundle the lib with its dependencies or bundle your custom code with the lib when actually using it.
  • Bad practice if you plan to release a npm module

State of the art (?): Modules

Typescript supports ES6 Modules, they are new and shiny and everybody seems to agree they are the way to go. The idea seems to be that each file is a module and by suppling the files in import statements you can define your dependencies very explicitly which makes it easy for bundling tools to efficently pack your code. I mostly have one class per file which doesn't seem to work to well with dhte module pattern.

This is my file structure after the refactor:

Also I'm having an index.ts file in each folder so I can import all of its classes by import * as FolderModule from "./folder"

export * from "./AggregateLoader";
export * from "./ImageLoader";
export * from "./TiledLoader";
export * from "./XhrLoaders";
export * from "./XmlSpriteFontLoader";

Example file - I think the problem becomes clearly visible here..

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
}

Example usage. As you can see I no longer have to walk through the namespaces, because I can import the classes directly.

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.
}

Pros

  • Makes your code more modular as its no longer bound to namespaces.
  • You can use bundling tools like browserfy or webpack, which can also bundle all your dependencies
  • You are more flexible when importing classes and no longer have to walk namespace chains.

Cons

  • 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)

IMO both approaches seem to flawed. Namespaces seem to be terribly outdated, impractical for large projects and incompatible with common tools while using modules is quite inconvenient and breaks some of the features I was adapting typescript for in the first place.

In a perfect world I would write my framework using the namespace pattern and export it as a module which then can be imported and bundled with its dependencies. However this doesn't seem to be possible without some ugly hacks.

So here's my question: How have you dealt with these problems? How can I minimize the drawbacks each approach implies?

Update

After gaining a bit more experience with typescript and javascript developement in general, I have to point out that modules are probably the way to go for 90% of all use cases.

The last 10% are hopefully legacy projects which use global namespaces, that you want to spice up with a little typescript (which works great by the way).

Much of my critic for modules can be (and has been) resolved by better IDE support. Visual Studio Code has since added automatic module resolution which works great.

解决方案

tl;dr: Do not choose the past. Choose the future: Modules.

In early drafts of the ES6 modules specification, there was an inline modules notion, which then 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 to namespaces in order to avoid confusion with the standard.

Namespaces are a legacy feature. It won't be a part of the language ECMAScript. And the TypeScript team will continue to follow the standard. There has been no improvement regarding TS namespaces since the release of the ECMAScript modules standard in July 2014.

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)

We can hope some improvements on these issues with future IDEs. The first one is already resolved by WebStorm.

这篇关于模块与命名空间:组织大型打字稿项目的正确方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆