Как разбить большой файл в node.js на несколько поменьше?

У меня есть файл в 900 строк. есть логически независимые участки, которые хотелось бы вынести в отдельные файлы. Но проблема в том, что все эти участки требуют множество объявленных в главном файле переменных. Если выносить их в отдельные модули, то нужно будет передавать им штук 10 параметров. Можно сделать их глобальными, но этот способ не нравится. Можно сделать один глобальный объект, куда сваливать эти переменные, тоже как то не красиво. Как сделать это правильно? (я в node.js новичок). Есть что то типа include "file_name.php" как в php, при вызове которого код из файла "вставляется" в место вызова?
  • Вопрос задан
  • 3325 просмотров
Решения вопроса 1
MarcusAurelius
@MarcusAurelius Куратор тега Node.js
автор Impress Application Server для Node.js
Этот вопрос более общий, чем Ваш случай с node.js и даже более общий, чем JavaScript вообще. Хорошо разделить свой код на части, выделить абстракции, дать им названия и объединить их в одно целое - это одна из главных задач программиста на любом языке.

Я сначала отвечу на частный вопрос по Node.js и JavaScript, а потом объясню эту тему глубже. Для Node.js есть require, при помощи которого можно импортировать из других модулей. Это реализация Dependency Lookup, т.е. мы имеем несколько модулей и каждый из них знает, от каких он зависит и может запросить менеджер зависимостей дать ссылку на то, что экспортирует другой модуль (в данном случае файл). Например, мы можем сделать require('./matrix.js') или require('./lib/matrix.js') и таким образом получим ссылку на то, что экспортирует matrix.js через module.exports = { ... };. Пример кода с импортом: names.js

В этот самый module.exports мы отдаем ссылку на функцию ил на объект или массив, который содержит все, что мы хотим экспортировать (можно экспортировать и скалярное значение, но это практически не нужно). В подавляющем большинстве случаев экспортируют объект (используя его как справочник экспортируемых идентификаторов), чуть реже одну функцию (иногда фабрику или функцию обертку) или конструктор прототипа (или класса). Пример кода с экспортом: lib/submodule2.js

Но можно импортировать зависимости целыми массивами, например тут: main.js Если посмотреть чуть шире ноды, на JavaScript, то есть способ импорта/экспорта через ключевые слова import и export. Примеры: import.mjs и export.mjs

Документацию модно почитать тут:
1. Модули для Node.js через require/module.exports: https://nodejs.org/api/modules.html
2. Модули для Node.js через import/export: https://nodejs.org/api/esm.html
3. Модули для JavaScript через import/export: https://developer.mozilla.org/en-US/docs/Web/JavaS... и https://developer.mozilla.org/en-US/docs/Web/JavaS...

Но это все техническая реализация, гораздо важнее то, как мы разбиваем наш код на модули или абстракции. Тут нужно ввести понятие связывание и классифицировать методы связывания (частей кода):
1. Через общие данные (самое жесткое связывание)
2. Через вызовы (обычно между модулями с экспортом и импортом)
3. Через события (самое легкое, между слабосвязанными программными компонентами)

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

Вызовы - это гораздо более безопасный способ взаимодействия программных компонентов, поэтому мы используем его для интерфейса между модулями и между классами и прототипами. Мы экспортируем/импортируем коллекции функций (API) или коллекции классов, что предполагает, что все взаимодействие между ними будет через вызовы (даже обращение к свойствам может быть перехвачено геттерами и сеттерами). Об этом способе связывания модулей я уже выше много сказал.

Наименее жесткий способ связывания - через события. Тут один программный компонент (модуль, класс, прототип, функтор) может подписаться на события другого или вызывать события у себя или в другого не ожидая, что там есть подписчики. Конечно тут могут быть проблемы с контрактами событий, т.е. именами и форматами данных, они все указаны не жестко и тут может быть много ошибок. Это очень гибкий способ, но он таит в себе потенциальные проблемы совместимости. Так что, где можно обойтись вызовами, то лучше не использовать события.

Есть еще один средний способ между вызовами и событиями, это callback (и listener). При помощи него события и сделаны, практически, в метод .on(name, callback) (или его аналоги) передается функция callback в качестве аргумента. На этом способе построено асинхронное программирование, в том числе такие способы связывания, как асинхронная последовательная композиция и асинхронная параллельная композиция функций, чеининг и промисы.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы