Модуль vs пространство имен - Импорт vs Требовать Typescript


102

Я часто путаюсь module/namespace/exportи import, require, referenceиспользую. Имея опыт работы с Java, может ли кто-нибудь вкратце объяснить мне, когда что использовать и каков правильный дизайн? Я чувствую, что ошибаюсь, когда пишу образец проекта

Насколько я понимаю, 1. moduleдля внешних пакетов 2. namespaceдля внутренних пакетов

  • Я не понял, как мы их классифицируем?
  • Когда экспортировать класс, пространство имен или пакет?
  • Если мы экспортируем пакет / пространство имен, все классы внутри него экспортируются или должны быть экспортированы явно.
  • Как каждый из них можно импортировать / потребовать?

Согласно документу , если я создаю каждый файл «ts» для каждого менеджера / модели, Typescript не рекомендует использовать «пространства имен»? Непосредственно использовать ссылочные пути?

Пожалуйста, объясните подробно, так как я пришел из другого опыта и не уверен в ES6 / ES5 и т. Д.

Я видел, как несколько человек задавали одни и те же вопросы или путались с ними. Надеюсь, кто-нибудь сможет подробно объяснить сценарий реального мира


у этого есть много внимания, которое отвечает на ваш вопрос, не уверен, почему он был награжден
— Дэн

@DanPantry, до начала награждения не было единого ответа :(
— Редди

моя ошибка - я не сравнивал метки времени.
— Дэн Пантри

Ответы:


80

Я не понял, как мы их классифицируем?

Пространства имен используются для организации / инкапсуляции вашего кода. Внешние модули используются для организации / инкапсуляции вашего кода И для поиска вашего кода во время выполнения. На практике у вас есть два варианта во время выполнения: 1) объединить весь транспилированный код в один файл или 2) использовать внешние модули и иметь несколько файлов и требовать некоторого другого механизма для доступа к этим файлам.

Когда экспортировать класс, пространство имен или пакет?

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

Если мы экспортируем пакет / пространство имен, все классы внутри него экспортируются или должны быть экспортированы явно.

Классы в пространстве имен всегда должны быть экспортированы явно, чтобы класс был виден во время компиляции за пределами файла, в котором он определен.

Как каждый из них можно импортировать / потребовать?

Это зависит от того, используете ли вы внешние модули. Внешний модуль всегда нужно будет импортировать, чтобы «использовать» его. Импорт пространства имен, которое не находится во внешнем модуле, на самом деле просто предоставляет псевдоним для пространства имен - вам все равно нужно префикс типа / любого другого псевдонимом (и именно поэтому вы обычно не хотите использовать пространства имен с внешними модулями; это означает, что вы всегда должны использовать префикс при ссылке на что-либо, предоставленное внешним модулем.) Пространства имен, которых нет во внешнем модуле, могут охватывать файлы, поэтому, если вы находитесь в том же пространстве имен, вы можете ссылаться на все, что экспортировано пространство имен без необходимости какого-либо импорта.

Чтобы действительно понять вышесказанное, вам потребуются некоторые базовые знания. Ключевым моментом, который нужно понимать со ссылками / пространствами имен / внешними модулями, является то, что эти конструкции делают во время компиляции и что они делают во время выполнения.

Справочные директивы используются во время компиляции для поиска информации о типе. В вашем источнике есть особый символ. Как компилятор TypeScript находит определение для этого символа? Ссылочная директива в значительной степени была включена в механизм tsconfig.json - используя tsconfig.json, вы сообщаете компилятору, где находятся все ваши источники.

Пространства имен могут содержать определения типов и / или реализацию. Если пространство имен содержит только информацию о типе, тогда оно вообще не имеет проявления во время выполнения - вы можете проверить это, посмотрев на вывод JS и найдя пустой файл JS. Если пространство имен имеет код реализации, то код помещается в закрытие, которое назначается глобальной переменной с тем же именем, что и пространство имен. Во вложенных пространствах имен для корневого пространства имен будет глобальная переменная. Опять же, проверьте вывод JS. Пространства имен исторически представляют собой то, как клиентские библиотеки JS пытались избежать проблемы с конфликтами имен. Идея состоит в том, чтобы обернуть всю вашу библиотеку в одно закрытие, а затем предоставить как можно меньший глобальный след - только одну глобальную переменную, ссылающуюся на закрытие. Что ж, проблема по-прежнему в том, что вы заявили о себе в глобальном пространстве. Что, если вам нужны, скажем, две версии библиотеки? Для пространства имен TypeScript все еще существует проблема, как найти источник для пространства имен. То есть исходный код, который ссылается на AB, по-прежнему имеет проблему с сообщением компилятору, как найти AB - либо с помощью ссылочных директив, либо с помощью tsconfig.json. Или поместив пространство имен во внешний модуль, а затем импортировав внешний модуль.

Внешние модули созданы на стороне сервера JS. Между внешним модулем и файлом в файловой системе существует взаимно однозначное соответствие. Вы можете использовать структуру каталогов файловой системы для организации внешних модулей во вложенную структуру. Импорт внешнего модуля, как правило, всегда вводит зависимость времени выполнения от этого внешнего модуля (исключение - когда вы импортируете внешний модуль, но затем не используете его экспорт в позиции значения - то есть вы импортируете только внешний модуль чтобы получить информацию о его типе). Внешний модуль неявно находится в замыкании, и это ключевой момент: пользователь модуля может назначить замыкание любой локальной переменной, которую он хочет. TypeScript / ES6 добавляет дополнительный синтаксис для сопоставления экспорта внешних модулей с локальными именами, но это всего лишь тонкость. На стороне сервера найти внешний модуль относительно просто: просто найдите файл, представляющий внешний модуль, в локальной файловой системе. Если вы хотите использовать внешние модули на стороне клиента, в браузере это становится более сложным, поскольку нет эквивалента файловой системе, в которой модуль доступен для загрузки. Итак, теперь на стороне клиента вам нужен способ объединить все эти файлы в форму, которая может использоваться удаленно в браузере - именно здесь бандлеры модулей, такие как Webpack (хотя Webpack делает намного больше, чем объединение модулей) и Browserify вступает в игру. Сборщики модулей позволяют разрешать внешние модули во время выполнения в браузере. в браузере он становится более сложным, поскольку нет эквивалента файловой системе, в которой модуль доступен для загрузки. Итак, теперь на стороне клиента вам нужен способ объединить все эти файлы в форму, которая может использоваться удаленно в браузере - именно здесь бандлеры модулей, такие как Webpack (хотя Webpack делает намного больше, чем объединение модулей) и Browserify вступает в игру. Сборщики модулей позволяют разрешать внешние модули во время выполнения в браузере. в браузере он становится более сложным, поскольку нет эквивалента файловой системе, в которой модуль доступен для загрузки. Итак, теперь на стороне клиента вам нужен способ объединить все эти файлы в форму, которую можно будет использовать удаленно в браузере - именно здесь бандлеры модулей, такие как Webpack (хотя Webpack делает намного больше, чем объединение модулей) и Browserify вступает в игру. Сборщики модулей позволяют разрешать внешние модули во время выполнения в браузере.

Сценарий реального мира: AngularJS. Представьте, что внешних модулей не существует, используйте единое пространство имен для ограничения загрязнения глобального пространства (в приведенном ниже примере единственная переменная MyApp - это все, что находится в глобальном пространстве), экспортируйте только интерфейсы и используйте внедрение зависимостей AngularJS для реализации доступны для использования. Поместите все классы в корень каталога, добавьте tsconfig.json в корень, установите angularjs typings в том же корне каталога, чтобы tsconfig.json тоже взял его, объедините весь вывод int в один JS файл. Это будет нормально работать для большинства проектов, если повторное использование кода не вызывает больших проблем.

MyService.ts:

namespace MyApp {

    // without an export the interface is not visible outside of MyService.ts
    export interface MyService { 
        ....
    }

    // class is not exported; AngularJS DI will wire up the implementation
    class MyServiceImpl implements MyService {
    }

    angular.module("MyApp").service("myService", MyServiceImpl);
}

MyController.ts:

namespace MyApp {

   class MyController {
       // No import of MyService is needed as we are spanning 
       // one namespace with multiple files.
       // MyService is only used at compile time for type checking. 
       // AngularJS DI is done on the name of the variable. 
       constructor(private myService: MyService) { 
       }
   }
   angular.module("MyApp").controller("myController", MyController);
}

Использование IIFE во избежание загрязнения глобальной среды выполнения. В этом примере вообще не создаются глобальные переменные. (Предполагается, что tsconfig.json.)

Foo.ts:

namespace Foo {
    // without an export IFoo is not visible. No JS is generated here
    // as we are only defining a type.
    export interface IFoo {
        x: string;
    }
}

interface ITopLevel {
    z: string;
}

(function(){
    // export required above to make IFoo visible as we are not in the Foo namespace
    class Foo1 implements Foo.IFoo {
        x: string = "abc";
    }
    // do something with Foo1 like register it with a DI system
})();

Bar.ts:

// alias import; no external module created
import IFoo = Foo.IFoo;

(function() {

    // Namespace Foo is always visible as it was defined at
    // top level (outside of any other namespace).
    class Bar1 implements Foo.IFoo {
        x: string;
    }

    // equivalent to above
    class Bar2 implements IFoo {
        x: string;
    }

    // IToplevel is visible here for the same reason namespace Foo is visible
    class MyToplevel implements ITopLevel {
        z: string;
    }

})();

Используя IIFE, вы можете отказаться от использования MyApp в качестве глобальной переменной в первом примере.

MyService.ts:

interface MyService { 
    ....
}

(function() {

    class MyServiceImpl implements MyService {
    }

    angular.module("MyApp").service("myService", MyServiceImpl);
})();

MyController.ts:

(function() { 

   class MyController { 
       constructor(private myService: MyService) { 
       }
   }

   angular.module("MyApp").controller("myController", MyController);
})();

30

Есть две вещи:

  • Модуль в машинописном является стандартным ES6 понятие, она использует import/ exportключевые слова на верхнем уровне кода;
  • Пространство имен - это понятие, специфичное для TypeScript, которое помогает организовать код устаревшим образом.

Пространства имён

Это почти устаревшее понятие. До модулей ES6 обычным способом разделения кода JavaScript в браузере было создание глобальных переменных. Например, все функции API, такие как подчеркивание, были размещены в глобальной переменной с именем _.

Этот старый способ работы похож на пакеты Java или пространства имен PHP. Он не адаптирован для Интернета. Новый стандарт ECMAScript решает такие вопросы, как: как использовать две библиотеки с одинаковыми именами? Как использовать две разные версии одной и той же библиотеки?

Примечание. В версиях TypeScript до определения «модулей» в ECMAScript (лето 2014 г.) пространства имен назывались «внутренними модулями», а модули назывались «внешними модулями».

Примечание 2: ключевое слово exportвнутри a namespace- это нестандартное использование ключевого слова TypeScript. Это средство объявить вещь, которая публично доступна извне пространства имен.

Модули ES6

Модуль - это файл, который содержит ключевые слова importили находится exportна верхнем уровне кода.

TypeScript следует стандарту ECMAScript. Предлагаю прочитать хорошее введение в модули ES6 в статье от Mozilla.

Если вы хотите использовать модули во внешнем приложении (в браузере), вам придется использовать сборщик ( Webpack [ документация здесь ], Browserify) или загрузчик ( SystemJS [ учебник здесь ], RequireJS) и для настройки TypeScript в этой среде.

Если ваш код выполняется в Node.js, просто настройте компилятор TypeScript для генерации формата CommonJS.

Примечание: пространство имен может быть объявлено в модуле. В этом случае он не будет доступен как глобальная переменная извне модуля. Однако его можно экспортировать из модуля.


Разве компилятор машинописного текста не объединяет модули автоматически в один файл .js?
— Kokodoko 01

@Kokodoko, Нет. Он может просто объединяться.
— Paleo

Эмм ... какая разница? .... Я использую модули и только tsc для создания одного файла .js. Мне не нужно использовать systemJS, webpack или browserify.
— Kokodoko 02

1
@Kokodoko Ты прав! Виноват! Кажется, что компилятор TypeScript может создавать бандлы, начиная с TS 1.8 , я пропустил эту функцию.
— Paleo

При этом ... Мои модули компилируются, но не запускаются после SystemJS.import ('/ js / main.js'); Есть идеи, как запустить приложение?
— Kokodoko 02

14
  1. модуль предназначен для внешних пакетов 2. пространство имен предназначено для внутренних пакетов

Фактически moduleключевое слово было заменено namespaceключевым словом.

Таким образом, лучше заявить, что модули - это то, что раньше называлось внешними модулями, пространство имен - это то, что раньше называлось внутренними модулями.

Больше

Надеюсь, это поможет в дальнейшем: https://basarat.gitbooks.io/typescript/content/docs/project/modules.html


8
извините за информацию, вопросы остались без ответа. Я ожидал подробного объяснения на примере из реальной жизни, а не на определении из учебника.
— Reddy

Вы имеете в виду, что в TypeScript сейчас нет модульной клавиатуры?
— Мохаммад Кермани

7

" require " и " import " эквивалентны по функциональности. мы можем использовать их как взаимозаменяемые, потому что у нас есть транспилеры, которым все равно, поддерживает их браузер изначально или нет. но, в то время как « require » уходит корнями в старый стиль кодирования, пришедший из CommonJS до 2009 года, «import» выводит свой синтаксис из широко принятого синтаксиса ES6 (ES2015). поэтому вы должны использовать « импорт » для новых проектов, а не « требовать ».


1
Именно. Так сбивает с толку и так мало информации об этом, говоря, что это в основном одно и то же. Интересно, почему это было отклонено?
— Шай Петель,

0

Путаница в именах

В первые дни Typescript пространства имен назывались внутренними модулями, а модули ES6 назывались внешними модулями .

Теперь для объявления пространств имен команда Typescript рекомендует использовать namespace { }вместо module { }синтаксиса, чтобы избежать путаницы в именах с внешними модулями. Потому что внешние модули теперь просто «модули», а внутренние модули - «пространства имен».


Пространства имён

Декларация

Пространство имен в Typescript может быть объявлено с помощью ключевого слова namespaceили moduleключевого слова. Оба ключевых слова делают одно и то же. Затем мы можем решить, какую часть нашего пространства имен сделать общедоступной, используя exportключевое слово.

// LivingThings.ts
export namespace Animals {
    export class Dog { }
    export class Cat { }
}
export namespace Plants {
    export class Orchid { }
    export class Bamboo { }
}

// LivingThingsUser.ts
import { Animals, Plants } from "./LivingThings"

Логическая группировка

До ES6 пространства имен использовались в Typescript для инкапсуляции интерфейсов, классов, функций и переменных для поддержки группы связанных функций и скрытия деталей реализации. Таким образом, мы могли предотвратить утечку переменных в глобальное пространство. Это помогло улучшить организацию кода и предотвратить конфликты имен. Теперь для этого рекомендуется использовать модули ES6.

Пространства имен теперь используются для объявлений внешнего пространства имен.

Использование одного файла

Мы можем объявить пространства имен для нескольких файлов, и их можно объединить с помощью --outFileфлага. Затем мы можем использовать этот объединенный файл внутри <script>тега на нашей HTML-странице. Это позволяет нам хорошо структурировать наш код в клиентском веб-приложении со всеми включенными зависимостями.


Модули

Декларация

Модули также называют модулями ES6. Мы используем несколько файлов для группировки связанных функций и просто используем exportключевое слово, чтобы сделать желаемый объект общедоступным.

// Animals.ts
export class Dog { }
export class Cat { }

// Plants.ts
export class Orchid { }
export class Bamboo { }

// LivingThingsUser.ts
import { Dog, Cat } from "./Animals"
import { Orchid, Bamboo } from "./Plants"

Логическая группировка

Логическая группировка в модулях достигается за счет использования отдельных файлов для группировки связанных функций. По этой причине внешние модули также называются файловыми модулями .

Использование одного файла

Мы не загружаем модули клиентского веб-приложения с помощью <script> тега, потому что браузеры могут работать медленно при одновременной загрузке большого количества файлов и визуализации страницы. Для этого мы используем загрузчики модулей, такие как CommonJS, AMD, SystemJS, которые позволяют нам загружать файлы асинхронно или объединять файлы внешних модулей в один оптимизированный файл.

На стороне сервера, особенно в Node.js, настоятельно рекомендуются модули.

Это оно!

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.