Авторский проект IT-специалиста Олега Барабанова Персональные публикации на тему IT и не только…

ES Modules — единственная модульная система которая должна остаться в современной JavaScript разработке

В прошлом месяце опубликовал статью, в которой рассказал о разработке JavaScript-полифила для псевдокласса :has(), в которой я задел тему проблем одновременной поддержки работы различных видов модулей в JavaScript и TypeScript. Т.к. это проблема еще очень актуальна, я решил немного рассказать своими словами, какие типы модулей все еще существуют в мире JavaScript и почему стоит заканчивать этот цирк и поддерживать только EcmaScript Modules.

Небольшой исторический экскурс по модульным системам в JS

В JavaScript до недавнего времени не существовало нативной поддерки разбиения кода на отдельные модули на уровне языка. Можно конечно упомянуть про поочередную загрузку через тег <script> в браузерах, но это всетаки относится не к JavaScript, а к браузерному API.

Но т.к. никто не мешает нам загружать по сети код и запускать его через тот же eval, логично что стали появляться касточные решения для обеспечения модульности:

В общем как видите, разработчикам на JavaScript раньше не приходилось скучать пр разработке больших приложений.

Использование модульных систем в современном JavaScript коде

Можно конечно в статье долго и нужно расписывать нюансы каждой из вышеперечисленных модульных систем, но этой информации и так в интернете полно, а я просто скажу вам одно - пишите новый код только с использованием современной ES Modules, ибо все остальные решения являются костылями на бедным JavaScript.

В настоящее время ESM широко поддерживается в современных браузерах, в т.ч. динамические импорты, а в NodeJS поддержка есть уже с 8-ой версии.

В старом коде, который приходится сопровождать, конечно придется пользоваться той модульной системой, которая там уже работает и к сожалению на текущий момент эта тема все еще актуальна.

NodeJS и ESM

Вопрос поддержки модульности в старом коде особо остро стоит в NodeJS. Хоть NodeJS и давно поддерживается ESM, там уже понаписано огромное количество кода под CommonJS, который вполне работает и не факт что этот код будет переписан под ESM. Поэтому NodeJS на текущее время вынужден официально поддерживать распространение кода как в формате CommonJS, так и ESM.

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

Ранее в NodeJS, подключать модули без указания расширения файла можно было добавив при запуске NodeJS параметр --experimental-specifier-resolution=node, но в 19-ой версии эту возможность убрали. Особенно эта проблема актуальна в случае с TypeScript, поскольку он при компиляции не модифицирует импорты, при том что в нем допустимы импорты без расширения файла, а указывать расширение .js в импортах TS выглядит жутко неестественно, а также иногда является.причиной неприятных сюрпризов. Проблему с импортами в скомпилированных ESM модулях позволяют в итоге решить различные бандлеры (Rollup, Webpack, ...), которые вполне умеют разруливать различные виды импортов (с расширением и без него). Но если вы в дальнейшем планируете использовать бандл с TypeScript, то помимо сборки бандла придется озаботиться сборкой единого файла деклараций .d.ts, что обычно решается отдельными плагинами к бандлам. В общем там немало проблем и при создании полифила я с многими из них столкнулся и хочу сказать, что тема с расширениями файлов в импортах TypeScript на самом деле очень широкая и с большим количеством неоднозначностей, что однозначно выходит за рамки текущей статьи.

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

UMD для старого кода

Тем не менее, хоть я и настаиваю на написании кода с использованием только ESM, для работы кода в окружении которое не поддерживает ESM (старые версии NodeJS и браузеров), вы вполне можете компилировать ESM модули в бандлы в виде универсального UMD модуля.

Почему именно UMD? Суть в том, что UMD является универсальным решением, которое может работать и как CommonJS и как AMD и соответственно может их заменить. Также UMD можно использовать напрямую в браузере через тег <script src="...">, без необходимости в каких-либо сторонних загрузчиках. В общем универсальное решение для работы с устаревшим кодом.

Заключение

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

Я помню как непомерно радовался первым нативным реализациям ES Modules в браузерах, поскольку это был довольно таки серьезный шаг в развитии JavaScript и по возможности я сразу стал использовать их в проектах и с тех пор ниразу не пожалел об этом решении.

Сейчас еще существуют проблемы совместной работы ESM и старых модульных систем, ибо как я уже говорил, старого кода уже понаписано много и он вполне работает. Но с момента первых реализаций ESM уже прошло достаточно много времени и проблема с каждым годом становится все менее актуальной и со временем окончательно исчезнет.

↑ ↓