Привет всем! Меня зовут Михаил, я старший Frontend-разработчик в НЛМК, занимаюсь разработкой одной из внутренних информационных систем на React + Typescript.
Расскажу про самый короткий и наименее трудоемкий способ экспорта и импорта модулей, что частенько требуется для построения современных приложений. А именно опишу свой эксперимент с импортом и экспортом без использования файла Index.ts, затем — с его использованием. Для наглядности я создал небольшой проект с Webpack и Typescript в редакторе исходного кода Visual Studio Code (далее по тексту VS Code).
Файл webpack.config.js содержит дефолтные настройки, никаких плагинов я не использую. В статье посмотрим на результаты сборки проекта с помощью Webpack в режиме “mode=development”, в режиме “mode=production” и затем подведем итоги.
Структура проекта выглядит следующим образом:
В app.ts происходит подключение модулей mod1, mod2, mod3. А модули mod1, mod2, mod3 внутри себя импортируют типы из папки types.ts.
Само приложение очень простое.
Объявляем две числовые константы. Вызываем функцию суммирования чисел из mod1, затем вызываем функцию из mod2, получаем строку с текстовым описанием проделанной операции и для вывода описания в консоль вызываем функцию из mod3.
Начнем рассмотрение без файлов Index.ts.
Содержимое файлов представлено ниже:
app.ts
mod1:
mod2:
mod3:
Типы простейшие используются для демонстрации:
caption_types:
resultMessage_types:
sumElement_types:
Видим в app.ts три строки импорта до каждого файла, т.е.
необходимо прописывать путь до файла;
проникновение во внутреннюю реализацию, в структуру и именование файлов внутри общей папки parts;
необходимость писать 3 отдельных импорта.
В файлах модулей mod1, mod2, mod3 импорт типов обладает всеми вышеописанными недостатками. Если бы происходил импорт более одного типа, то каждый тип импортировался бы отдельной строкой и напрямую зависел бы от расположения файла с типом внутри папки types. Т.е., если внутри папки types возникнет необходимость перенести файл с экспортируемым типом в другую папку или перенести экспортируемый тип в другой файл, во всех модулях, импортирующих этот тип, придется внести изменения.
Проведем сборку с помощью Webpack в режиме “mode=development”, в режиме “mode=production”.
-
mode=development:
Сборка прошла за 3380 миллисекунд, результирующий файл main.js весит 6.16 килобайт.
mode=production:
Сборка прошла за 3622 миллисекунды, результирующий файл main.js весит 50 байт.
Запомним эти результаты и теперь добавим в проект файлы Index.ts.
Первым делом видим структуру проекта, которая явно увеличилась.
Посмотрим, что содержат файлы Index.ts в папках parts и types:
parts/Index.ts:
types/Index.ts:
Благодаря файлам Index.ts в папках с модулями mod1, mod2, mod3 и в папках с типами caption, resultMessage, sumElement не нужно писать что-то типа export * from './mod1/mod1' - видим задвоение /mod1/mod1.
Посмотрим, как изменился наш файл app.ts:
В app.ts:
нет необходимости прописывать путь вплоть до файла;
мы ничего не знаем про внутреннюю реализацию и структуру файлов внутри общей папки parts;
одна строка импорта для функций через именованный деструктурированный импорт.
Теперь импорты типов в mod1, mod2, mod3 напрямую не зависят от расположения файла с типом внутри папки types и имени самого файла, экспортирующего данный тип.
Для примера переименуем файл с типом CaptionItem из “caption_types” в “renamed_caption_types”. Единственной точкой внесения изменений будет файл Index.ts внутри самой папки caption, содержащей переименованный файл.
Таким образом, файлы индекса служат «шлюзом» для всего импорта, связанного с папкой, в которой они находятся. Это своего рода “оглавление” экспортов папки.
Экспорт через файл Index.ts защищает от экспорта типов или переменных с одинаковыми названиями, но возможно с разным содержимым.
Например, в папке types/caption_types находится тип CaptionItem, который равен number.
Добавим в файл resultMessage_types тип с аналогичным названием CaptionItem, но приравняем его string.
После это VS Code выдаст подсказку, что экспортируемый ранее модуль уже экспортирует значение с аналогичным названием.
Импорт типа CaptionItem происходит в модуле mod2, удалим импорт и посмотрим какие варианты для импорта предложит VS Code.
VS Code выдает подсказку для импорта, и разработчик может принять неверное решение - импортировать тип из файла resultMessage_types, а потом гадать, что же происходит. Особенно актуально это на больших проектах. Файл Index.ts позволит избежать этих проблем.
Проведем сборку с помощью Webpack в режиме “mode=development” и в режиме “mode=production” с файлами Index.ts.
mode=development:
Сборка прошла за 3519 миллисекунд, результирующий файл main.js весит 9.02 килобайта.
Без Index.ts файлов сборка в режиме “development” прошла за 3380 миллисекунд и сформировала результирующий файл main.js размером 6.16 килобайт, значит в режиме разработки файлы индекса увеличивают время сборки и попадают в результирующий бандл main.js. Это плохие новости.
Попробуем собрать в режиме “production”.
-
mode=production:
Сборка прошла за 3915 миллисекунд, результирующий файл main.js весит 50 байт.
Без Index.ts файлов сборка в режиме “production” заняла 3622 миллисекунды, а результирующий бандл весил 50 байт, значит добавление файлов индекса увеличивает время сборки, но в результате обработки файлы индекса не попадают в результирующий бандл main.js., а значит не происходит увеличение бандла, который в итоге будет использоваться в “production”.
Подведем итоги. Выделим + и - использования файлов Index.ts.
Минусы:
Приходится создавать лишние файлы;
Файл проходит через все стадии компиляции;
Увеличивается время сборки;
При сборке в режиме “mode=development” включаются в бандл, увеличивая его размер.
Плюсы:
Пропадает необходимости прописывать путь вплоть до файла;
При импорте из Index.ts мы не завязаны на внутреннюю реализацию и структуру файлов внутри общей папки, из которой происходит экспорт. Это дает нам больше свободы, гибкости в случаях, когда необходимо переименовать, перенести папку или файл, содержащий экспортируемое значение;
При экспорте через файл Index.ts мы защищены от экспорта типов или переменных с одинаковыми названиями. И это может быть банальным задвоением, копированием, а ведь содержимое может быть разным…;
Значительно уменьшается количество строк импорта;
Облегчаем поиск файлов для импорта и делаем их более однозначными.
При сборке в режиме “mode=production” файлы Index.ts не включаются в бандл, не увеличивают размер бандла.
Итого:
На мой взгляд, умеренное использование файла Index.ts в разработке несомненно дает исключительно положительный эффект, если его использовать как “оглавление” для большой директории, содержащей типы, компоненты, модули приложения.
Самое главное, что при сборке для “production” наличие данных файлов не влияет на размер бандла.
В режиме “development” увеличение размера бандла и времени пересборки не столь критично, сколько польза от использования данного файла.
Спасибо за внимание! Пишите комментарии, буду рад узнать Ваше мнение по данному вопросу!
Комментарии (9)
daemonyeen
24.01.2023 11:02+1Современные сборщики вполне умеют вырезать из бандла все, что в нем не используется с помощью механизма TreeShaking, поэтому в плане увеличения размера бандла index.ts никак не влияет.
Для меня самым большим недостатком index.ts являются цикличиские зависимости. Причем они не начнут появляться, пока ваш проект не вырастет, и исправление может доставить неиллюзорные проблемы.
DreamShaded
24.01.2023 11:31вебпаковский плагин хорошо их отслеживает, можно его на старте подрубить и не допускать появления циклических зависимостей.
Ещё можно такую штуку использовать, удобно
daemonyeen
24.01.2023 11:41Конечно, и представляешь как обидно, когда тебе пишется цепочка из 10 файлов index.ts, где на каком то этапе образовалась циклическая зависимость? Добро пожаловать в приключение, где тебе надо скакать по всем 10ти и искать в каком именно, потому что зависимость могла возникнуть, например, в импорте пятого файла цепочки, но не в самом пятом файле. А потом мучительно менять импорты во всех остальных, чтобы это все исправить.
Проще отказаться от index.ts вовсе, за редким исключением:
Папки с множеством простых функций с нулевой связностью (например, helpers)
Высокоуровневого index.ts который работает на public api
DreamShaded
24.01.2023 12:21+1не, не представляю, на самом деле) три достаточно весомых проекта с десятком индексовых без циркулярок были. Может, не со старта запустили?
UnnamedVip Автор
24.01.2023 14:14+1Проблема легко решается. Чтобы избежать циклических зависимостей между файлами лежащими внутри проиндексированной дерриктории, нужно импортировать зависимости через относительный путь, в обход индекс файла. Индексный файл служит для импорта за пределами индексируемой дерриктории.
AlanRow
24.01.2023 12:28Способ измерения и их количество вызывают некоторые сомнения. Один раз измерили тут, один раз тут - получилось больше, значит причина в index.ts. По хорошему бы замерять это раз 10 хотя бы в разных условиях, на нескольких примерах и смотреть на среднее отклонение в процентах. Ведь на время сборки влияет не только структура кода, но и загруженность устройства иными процессами в данный момент
UnnamedVip Автор
24.01.2023 14:15Это было проделано несколько раз. Из изменений было только добавление и удаление Idex-файлов. Результат на всех итерациях был +- равен приведёному.
mariner
index.ts - это публичное API вашего модуля. С этой точки зрения (комплиментарной пункту 2 из плюсов) этот файл весьма необходим.
UnnamedVip Автор
Согласен. Необходим и очень удобен при разработке.