Модули и пространства имен: Как правильно организовать большой проект машинописного текста?

Я довольно новичок в машинописи и пишу небольшой фреймворк для создания прототипов для WebGl. В настоящее время я занимаюсь рефакторингом своего проекта и столкнулся с некоторыми проблемами, связанными с организацией своего проекта, поскольку оба подхода (модули и пространства имен), похоже, имеют серьезные недостатки.

Этот пост не о том, КАК использовать эти шаблоны, а о том, как преодолеть проблемы, которые приносит каждый из них.

Status Quo: использование пространств имен

Исходя из C# это казалось самым естественным путем. Каждый класс / модуль получает свое соответствующее пространство имен, и я предоставляю параметр "outFile" в файле tsconfig.json, чтобы все было объединено в один большой файл. После компиляции у меня есть корневое пространство имен как глобальный объект. Зависимости не встроены в проект, поэтому вам нужно вручную предоставить необходимые файлы *.js в html (не очень хорошо)

Пример файла

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

Пример использования (Вы должны убедиться, что пространство имен Cross загружено в глобальное пространство имен, предварительно подав файл js в html)

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

        //code omitted
    }
}

Pros

  • Прост в использовании, если вы переходите с C#/Java
  • Независимо от имен файлов - переименование файлов не нарушит ваш код.
  • Простота рефакторинга: IDE могут легко переименовывать пространства имен / классы, и изменения будут применяться последовательно во всем коде.
  • Удобство: добавить класс в проект так же просто, как добавить файл и объявить его в нужном пространстве имен.

Cons

Для большинства проектов мы рекомендуем использовать внешние модули и использовать пространство имен для быстрых демонстраций и переноса старого кода JavaScript.

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

  • Корневое пространство имен всегда (?) Глобальный объект (плохо)
  • Нельзя (?) Использовать с такими инструментами, как browserify или webpack, которые необходимы для связывания библиотеки с ее зависимостями или связывания вашего пользовательского кода с библиотекой при ее фактическом использовании.
  • Плохая практика, если вы планируете выпустить модуль npm

Состояние дел (?): Модули

Typescript поддерживает модули ES6, они являются новыми и блестящими, и все, кажется, согласны с тем, что они - путь. Похоже, идея заключается в том, что каждый файл является модулем, и, добавляя файлы в операторы импорта, вы можете очень четко определить свои зависимости, что упрощает объединение инструментов для эффективной упаковки вашего кода. В основном у меня есть один класс на файл, который, кажется, не очень хорошо работает с шаблоном модуля 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.
}

Pros

  • Делает ваш код более модульным, так как он больше не связан с пространствами имен.
  • Вы можете использовать инструменты объединения, такие как browserfy или webpack, которые также могут объединять все ваши зависимости
  • Вы более гибки при импорте классов и вам больше не нужно обходить цепочки пространств имен.

Cons

  • Очень утомительно, если каждый класс представляет собой отдельный файл, вам придется вводить одни и те же операторы импорта снова и снова.
  • Переименование файлов нарушит ваш код (плохо).
  • Рефакторинг имен классов не будет распространяться на ваш импорт (очень плохо - может зависеть от вашей IDE, хотя я использую vs-code)

ИМО оба подхода кажутся ошибочными. Пространства имен кажутся ужасно устаревшими, непрактичными для больших проектов и несовместимыми с обычными инструментами, в то время как использование модулей довольно неудобно и нарушает некоторые функции, для которых я вначале адаптировал машинопись.

В идеальном мире я написал бы свой фреймворк, используя шаблон пространства имен, и экспортировал бы его как модуль, который затем можно было бы импортировать и связать с его зависимостями. Однако это не представляется возможным без некоторых уродливых хаков.

Итак, вот мой вопрос: как вы справились с этими проблемами? Как я могу минимизировать недостатки каждого подхода?

Обновить

Получив немного больше опыта в разработке машинописи и JavaScript в целом, я должен отметить, что модули, вероятно, являются подходящим вариантом для 90% всех вариантов использования.

Надеемся, что последние 10% - это унаследованные проекты, использующие глобальные пространства имен, которые вы хотите оживить небольшим количеством машинописного текста (который, кстати, прекрасно работает).

Большая часть моей критики в отношении модулей может быть (и была) решена лучшей поддержкой IDE. Visual Studio Code с тех пор добавил автоматическое разрешение модуля, которое прекрасно работает.

1 ответ

Решение

tl;dr: не выбирай прошлое. Выберите будущее: Модули.

В ранних проектах спецификации модулей ES6 существовало понятие встроенных модулей, которое затем было исключено в сентябре 2013 года. Однако это понятие было уже реализовано командой TypeScript в 2012 году с первыми бета-версиями языка: это были внутренние модули. Затем окончательная спецификация для модулей ES6 была выпущена в июле 2014 года без встроенных модулей. Год спустя, в июле 2015 года, в версии 1.5 TypeScript внутренние модули были переименованы в пространства имен, чтобы избежать путаницы со стандартом.

Пространства имен являются устаревшей функцией. Он не будет частью языка ECMAScript. И команда TypeScript продолжит следовать стандарту. С момента выпуска стандарта модулей ECMAScript в июле 2014 года не произошло никаких улучшений в отношении пространств имен TS.

Минусы [из модулей ES6]

  • Очень утомительно, если каждый класс представляет собой отдельный файл, вам придется вводить одни и те же операторы импорта снова и снова.
  • Переименование файлов нарушит ваш код (плохо).
  • Рефакторинг имен классов не будет распространяться на ваш импорт (очень плохо - может зависеть от вашей IDE, хотя я использую vs-code)

Мы можем надеяться на некоторые улучшения по этим вопросам в будущих IDE. Первый уже решен с помощью WebStorm.

Другие вопросы по тегам