Изучите управление зависимостями и циклические зависимости в JavaScript

Node.js внешний интерфейс JavaScript
Изучите управление зависимостями и циклические зависимости в JavaScript

Обычно мы записываем сторонние зависимости, используемые в проекте, в файл package.json, а затем используем популярные инструменты управления зависимостями, такие как npm, cnpm или yarn, которые помогают нам управлять этими зависимостями. Но как они управляют этими зависимостями, в чем между ними разница и как разрешать циклические зависимости, если они возникают.

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

Семантическое управление версиями

При использовании сторонних зависимостей обычно необходимо указать диапазон версий зависимостей, например

"dependencies": {
    "antd": "3.1.2",
    "react": "~16.0.1",
    "redux": "^3.7.2",
    "lodash": "*"
  }

Приведенный выше файл package.json показывает, что номер версии antd, используемой в проекте, — 3.1.2, но в чем разница между 3.1.1 и 3.1.2, 3.0.1, 2.1.1. Правила семантического управления версиями предусматривают следующий формат версии: основной номер версии, вспомогательный номер версии, номер редакции, а правила увеличения номеров версий следующие:

  • Основной номер версии: когда вы вносите несовместимые изменения API
  • Второстепенный номер версии: когда вы делаете обратно совместимые функциональные дополнения
  • Номер редакции: когда вы делаете исправления обратной совместимости

Обновление основного номера версии обычно означает основные изменения и обновления.После обновления основной версии ваша программа может сообщить об ошибке, поэтому вам нужно быть осторожным при обновлении основного номера версии, но это часто повышает производительность и удобство работы. Обновление номера младшей версии обычно означает добавление некоторых функций, например, версия antd обновлена ​​с 3.1.1 до 3.1.2, предыдущий компонент Select не поддерживает функцию поиска, но после обновления поддерживается функция поиска. Обновление номера версии обычно означает исправление некоторых ошибок. Таким образом, дополнительный номер версии и номер редакции следует поддерживать в актуальном состоянии, чтобы вы могли получать последние функции, не сообщая об ошибках в предыдущем коде.

Однако часто мы не указываем конкретную версию зависимости, а указываем диапазон версий, таких как react, redux и lodash, в файле package.json выше, Эти три зависимости используют три символа для обозначения диапазона версий зависимости. Спецификации области семантического управления версиями:

  • ~: обновлять только номера ревизий
  • ^: Обновить дополнительный номер версии и номер редакции.
  • *: Обновите до последней версии

Таким образом, диапазоны версий зависимостей, установленных вышеуказанным файлом package.json, следующие:

  • реагировать@~16.0.1: >=реагировать@16.0.1 &&
  • redux@^3.7.2: >=redux@3.7.2 &&
  • lodash@*:lodash@последний

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

управление зависимостями

В разработке проектов обычно используетсяnpm,yarnилиcnpmЧтобы управлять зависимостями в проекте, давайте посмотрим, как они помогают нам управлять этими зависимостями.

npm

npmНа данный момент можно сказать, что он претерпел три основных изменения версии.

npm v1

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

"dependencies": {
    A: "1.0.0",
    C: "1.0.0",
    D: "1.0.0"
}

Все эти модули зависят от модуля B, и версия зависимого модуля B отличается.

A@1.0.0 -> B@1.0.0
C@1.0.1 -> B@2.0.0
D@1.0.0 -> B@1.0.0

выполнивnpm installКоманда, каталог node_modules, сгенерированный npm v1, выглядит следующим образом:

node_modules
├── A@1.0.0
│   └── node_modules
│       └── B@1.0.0
├── C@1.0.0
│   └── node_modules
│       └── B@2.0.0
└── D@1.0.0
    └── node_modules
        └── B@1.0.0

Очевидно, что под каждым модулем будет каталог node_modules для хранения прямых зависимостей модуля. Также будет каталог node_modules под зависимостями модуля для хранения зависимостей зависимостей модуля. Очевидно, что такой вид управления зависимостями прост и понятен, но есть большая проблема.Помимо глубокой вложенности длины каталога node_modules, это также вызовет проблему множественных копий одного и того же хранилища зависимостей.Например, B@1.0.0 выше хранится Это явно пустая трата денег на двоих. Итак, после выпуска npm v3 в управление зависимостями npm были внесены серьезные изменения.

npm v3

Для тех же вышеперечисленных зависимостей используйте npm v3 для выполненияnpm installКаталог node_modules, сгенерированный после команды, выглядит следующим образом:

node_modules
├── A@1.0.0
├── B@1.0.0
└── C@1.0.0
    └── node_modules
        └── B@2.0.0
├── D@1.0.0

Очевидно, что npm v3 использует плоский режим, размещая все модули и зависимости модулей, используемые в проекте, на верхнем уровне каталога node_modules и сохраняя их в каталоге node_modules под модулем только в случае конфликта версий. Зависимости, требуемые этим модулем . Причина, по которой это может быть достигнуто, основана на механизме поиска пакетов. Механизм поиска пакетов означает, что когда вы напрямуюrequire('A'), он сначала будет искать каталог node_modules в текущем пути, чтобы увидеть, существует ли зависимость.Если она не существует, он будет искать вверх, то есть продолжит поиск node_modules в верхнем каталоге пути. Из-за этого npm v3 может сгладить предыдущую структуру вложенности и поместить все зависимости в node_modules корневого каталога проекта, тем самым избегая проблемы слишком глубокой вложенности каталога node_modules. Кроме того, npm v3 также разрешает несколько версий зависимостей модулей в одну версию.Например, если A зависит от B@^1.0.1, а D зависит от B@^1.0.2, будет только одна версия B. @1.0.2. . Хотя npm v3 решает эти две проблемы, в настоящее время с npm все еще остается много проблем, наиболее критикуемой из которых должна быть его неопределенность.

npm v5

Что такое определенность. В контексте управления пакетами JavaScript детерминизм означает постоянное получение согласованной структуры каталогов node_modules с учетом файлов package.json и блокировки. Проще говоря, выполняется в любой средеnpm installОба получают одинаковую структуру каталогов node_modules. Для решения этой проблемы был создан npm v5.Каталог node_modules, сгенерированный npm v5, такой же, как и в v3.Разница в том, что v5 по умолчанию создает файл package-lock.json, чтобы гарантировать достоверность установленных зависимостей. Например, для следующего файла package.json

"dependencies": {
    "redux": "^3.7.2"
  }

Содержимое соответствующего файла package-lock.json выглядит следующим образом:

{
  "name": "test",
  "version": "1.0.0",
  "lockfileVersion": 1,
  "requires": true,
  "dependencies": {
    "js-tokens": {
      "version": "3.0.2",
      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
      "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
    },
    "lodash": {
      "version": "4.17.4",
      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
      "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4="
    },
    "lodash-es": {
      "version": "4.17.4",
      "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.4.tgz",
      "integrity": "sha1-3MHXVS4VCgZABzupyzHXDwMpUOc="
    },
    "loose-envify": {
      "version": "1.3.1",
      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
      "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=",
      "requires": {
        "js-tokens": "3.0.2"
      }
    },
    "redux": {
      "version": "3.7.2",
      "resolved": "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz",
      "integrity": "sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==",
      "requires": {
        "lodash": "4.17.4",
        "lodash-es": "4.17.4",
        "loose-envify": "1.3.1",
        "symbol-observable": "1.1.0"
      }
    },
    "symbol-observable": {
      "version": "1.1.0",
      "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.1.0.tgz",
      "integrity": "sha512-dQoid9tqQ+uotGhuTKEY11X4xhyYePVnqGSoSm3OGKh2E8LZ6RPULp1uXTctk33IeERlrRJYoVSBglsL05F5Uw=="
    }
  }
}

Нетрудно заметить, что файл package-lock.json записывает определенную версию каждой установленной зависимости, так что эту же зависимость можно установить через этот файл при следующей установке.

image

yarn

yarnОн был открыт в 2016.10.11, и пряжа появилась для решения некоторых проблем в npm v3, когда npm v5 еще не был выпущен. пряжа определяется как быстрое, безопасное и надежное управление зависимостями.

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

Структура каталогов node_modules, созданная yarn, такая же, как и в npm v5, а файл yarn.lock создается по умолчанию. В приведенном выше примере содержимое файла yarn.lock, созданного путем установки только зависимостей redux, выглядит следующим образом:

# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1

js-tokens@^3.0.0:
  version "3.0.2"
  resolved "http://registry.npm.alibaba-inc.com/js-tokens/download/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"

lodash-es@^4.2.1:
  version "4.17.4"
  resolved "http://registry.npm.alibaba-inc.com/lodash-es/download/lodash-es-4.17.4.tgz#dcc1d7552e150a0640073ba9cb31d70f032950e7"

lodash@^4.2.1:
  version "4.17.4"
  resolved "http://registry.npm.alibaba-inc.com/lodash/download/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"

loose-envify@^1.1.0:
  version "1.3.1"
  resolved "http://registry.npm.alibaba-inc.com/loose-envify/download/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
  dependencies:
    js-tokens "^3.0.0"

redux@^3.7.2:
  version "3.7.2"
  resolved "http://registry.npm.alibaba-inc.com/redux/download/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b"
  dependencies:
    lodash "^4.2.1"
    lodash-es "^4.2.1"
    loose-envify "^1.1.0"
    symbol-observable "^1.0.3"

symbol-observable@^1.0.3:
  version "1.1.0"
  resolved "http://registry.npm.alibaba-inc.com/symbol-observable/download/symbol-observable-1.1.0.tgz#5c68fd8d54115d9dfb72a84720549222e8db9b32"

Нетрудно заметить, что файл yarn.lock отличается от файла package-lock.json, сгенерированного npm v5, в следующих моментах:

  1. Формат файла другой, npm v5 использует формат json, пряжа использует пользовательский формат
  2. Все версии зависимостей, записанные в файле package-lock.json, являются детерминированными, и символы диапазона semver (~ ^ *) не будут отображаться, в то время как символы диапазона semver по-прежнему будут отображаться в файле yarn.lock.
  3. Файл package-lock.json богаче по содержанию: npm v5 нужен только файл package.lock для определения структуры каталогов node_modules, в то время как yarn должен полагаться на файлы package.json и yarn.lock для определения структуры каталогов node_modules. .

Относительно того, почему существуют эти различия, детерминированный алгоритм пряжи и отличие от npm v5, официальная статья о пряже подробно описывает эти моменты. Из-за ограниченного места я не буду здесь вдаваться в подробности, если вам интересно, вы можете перейти к моей статье о переводе.Пряжа Уверенностьиди и смотри.

yarnВ дополнение к улучшению скорости установки, самый большой вклад состоит в том, чтобы обеспечить уверенность в зависимости от установочных зависимостей через файл блокировки, чтобы обеспечить тот же пакет. Это те же структура каталогов Node_Modules. Это в значительной степени избегает некоторых «Что работает на моем компьютере, не удается на других» ошибках. Но при использовании пряжи для управления зависимостями вам все равно нужно обратить внимание на следующие 3 балла.

  • Не изменяйте вручную файл yarn.lock
  • Файл yarn.lock должен быть помещен в репозиторий с контролем версий.
  • При обновлении зависимостей используйтеyarn upgradeчтобы избежать ручного изменения файлов package.json и yarn.lock.

cnpm

cnpmПользователей в Китае должно быть довольно много, особенно тех, кому нужно построить частный склад. cnpm использует следующее при установке зависимостейnpminstallПроще говоря, cnpm использует метод установки по ссылке, чтобы максимизировать скорость установки, а сгенерированный каталог node_modules использует другой макет, чем npm. Пакеты, установленные с помощью cnpm, именуются в папке node_modules с номером версии @package name, а затем мягко связываются с папкой, названной только именем пакета. В том же примере структура каталогов node_modules, созданная при использовании cnpm для установки только избыточных зависимостей, выглядит следующим образом:

image

Самая большая разница между cnpm и npm и yarn заключается в том, что сгенерированная структура каталогов node_modules отличается, что может вызвать некоторые проблемы в некоторых сценариях. Кроме того, файлы блокировки не генерируются, что делает его немного менее надежным, чем npm и yarn с точки зрения надежности установки. Тем не менее, метод установки ссылок, используемый cnpm, по-прежнему очень хорош, что не только экономит место на диске, но и сохраняет четкость структуры каталогов node_modules.Можно сказать, что он нашел баланс между вложенным режимом и плоским режимом.

npm, yarn и cnpm обеспечивают хорошее управление зависимостями, помогая нам управлять различными зависимостями и версиями, используемыми в проекте, но если в зависимости есть циклический вызов, то как решить циклическую зависимость?

круговая зависимость

Циклическая зависимость означает, что выполнение модуля а зависит от модуля b, который, в свою очередь, зависит от модуля а. Циклические зависимости могут привести к рекурсивной загрузке, что при неправильном обращении может сделать выполнение программы невозможным. Прежде чем мы перейдем к циклическим зависимостям, давайте взглянем на спецификацию модуля в JavaScript. Потому что разные спецификации по-разному относятся к циклическим зависимостям.

В настоящее время общие спецификации JavaScript можно разделить на три типа:CommonJS,AMDа такжеES6.

Спецификация модуля

CommonJS

С момента появления node.js в 2009 г.CommonJSМодульная система постепенно набирает популярность. Модуль CommonJS представляет собой файл сценария, черезrequireкоманда для загрузки этого модуля и использования интерфейса, предоставляемого модулем. Выполнение во время загрузки — важная особенность модулей CommonJS, то есть код скриптаrequireКогда код в модуле выполняется. Эта функция хороша на стороне сервера, но если модуль введен, он будет ждать завершения его выполнения, прежде чем выполнять следующий код, что будет большой проблемой на стороне браузера. Отсюда появилсяAMDспецификация для поддержки среды браузера.

AMD

AMDЭто аббревиатура от «Определение асинхронного модуля», что означает «определение асинхронного модуля». Он использует асинхронную загрузку для загрузки модулей, и загрузка модулей не влияет на выполнение следующих за ней операторов. Все операторы, которые зависят от этого модуля, определяются в функции обратного вызова, которая не будет выполняться до тех пор, пока загрузка не будет завершена. Наиболее репрезентативной реализацией являетсяrequirejs.

ES6

В отличие от CommonJS и схемы загрузки модулей AMD,ES6Функционал модуля реализован на уровне языка JavaScript. Идея его дизайна состоит в том, чтобы сделать его как можно более статичным, чтобы зависимости модулей можно было определить во время компиляции. При встрече с командой загрузки модуляimportКогда модуль не выполняется, генерируется только ссылка. Подождите, пока он вам действительно понадобится, а затем перейдите к модулю, чтобы получить значение. Это самое большое отличие от спецификации модуля CommonJS.

Решение циклических зависимостей в CommonJS

См. пример ниже:

a.js

console.log('a starting');
exports.done = false;
const b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');

b.js

console.log('b starting');
exports.done = false;
const a = require('./a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');

main.js

console.log('main starting');
const a = require('./a.js');
const b = require('./b.js');
console.log('in main, a.done=%j, b.done=%j', a.done, b.done);

В этом примере модуль a вызывает модуль b, а модуль b должен вызывать модуль a, что создает циклическую зависимость между a и b, но когда мы выполняемnode main.jsКод не попадает в вызов бесконечного цикла, а выводит следующее:

$ node main.js
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done=true, b.done=true

Почему программа не сообщает об ошибке, а выводит вышеуказанное содержимое? Это связано с двумя особенностями модулей CommonJs. Во-первых, он выполняется при загрузке, во-вторых, загруженный модуль будет закэширован и не будет загружаться повторно. Проанализируем процесс выполнения программы:

  1. main.js выполняется, вывод основного запуска
  2. main.js загружает a.js, выполняет a.js и выводит start, export done = false
  3. a.js загружает b.js, выполняет b.js и выводит запуск b, экспорт выполнен = false
  4. b.js загружает a.js. Поскольку a.js был загружен один раз, он не будет загружаться повторно. Done = false экспортируется a.js в кеш, поэтому вывод b.js находится в b, a.done = ложь
  5. экспорт b.js выполнен = true, а вывод b выполнен
  6. После выполнения b.js право на выполнение возвращается к a.js, выполняется a.js и выводится в a, b.done = true
  7. a.js экспортирует done = true и выводит done
  8. После выполнения a.js право на выполнение возвращается main.js, а main.js загружает b.js, так как b.js уже был загружен один раз, он не будет выполняться повторно.
  9. вывод main.js в main, a.done=true, b.done=true

Из приведенного выше процесса выполнения мы видим, что в спецификации CommonJS при встречеrequire()Когда оператор будет выполнен, код в модуле require будет выполнен, а результат выполнения будет закеширован.Когда он будет загружен снова в следующий раз, выполнение не будет повторяться, а будет непосредственно извлечен закешированный результат. Из-за этого нет бесконечного цикла вызовов, когда есть циклическая зависимость. Хотя этот механизм загрузки модуля может избежать ситуации, когда циклические зависимости сообщают об ошибках, небольшая небрежность может привести к тому, что код не будет выполняться так, как мы предполагали. Поэтому при написании кода требуется тщательное планирование, чтобы гарантировать правильную работу зависимостей циклических модулей (официальный исходный текст: требуется тщательное планирование, чтобы позволить зависимостям циклических модулей правильно работать в приложении).

Есть ли способ избежать циклических зависимостей, кроме тщательного планирования? Менее элегантный метод заключается в том, чтобы сначала написать оператор exports, а затем написать оператор require в каждом модуле циклической зависимости, используя механизм кэширования CommonJS, вrequire()Перед другими модулями экспортируйте содержимое, которое будет экспортироваться самостоятельно, чтобы гарантировать, что другие модули могут получить правильное значение при их использовании. Например:

A.js

exports.done = true;

let B = require('./B');
console.log(B.done)

B.js

exports.done = true;

let A = require('./A');
console.log(A.done)

Этот метод написания прост и понятен, но недостатком является то, что метод написания каждого модуля нужно менять, и большинство студентов привыкли писать оператор require в начале файла.

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

Решение циклических зависимостей в ES6

Чтобы узнать решение циклических зависимостей в ES6, вы должны сначала понять механизм загрузки модулей ES6. Мы все знаем, что ES6 используетexportкоманда для указания внешнего интерфейса модуля, используйтеimportКоманда загрузки модуля. Так что же произошло тогда перед лицом импорта и экспорта? Механизм загрузки модулей ES6 можно описать двумя словами.будь спокоен.

  • Один статический: импортировать статическое выполнение
  • Один шаг: экспортировать динамическую привязку

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

Давайте посмотрим на пример:

foo.js

console.log('foo is running');
import {bar} from './bar'
console.log('bar = %j', bar);
setTimeout(() => console.log('bar = %j after 500 ms', bar), 500);
console.log('foo is finished');

bar.js

console.log('bar is running');
export let bar = false;
setTimeout(() => bar = true, 500);
console.log('bar is finished');

воплощать в жизньnode foo.jsвыведет следующее:

bar is running
bar is finished
foo is running
bar = false
foo is finished
bar = true after 500 ms

Отличается ли оно от того, что вы думаете? когда мы выполняемnode foo.jsВывод первой строки — это не первая консольная инструкция foo.js, а консольная инструкция bar.js. Это связано с тем, что команда импорта выполняется на этапе компиляции и статически анализируется движком JavaScript перед запуском кода, поэтому она имеет приоритет над содержимым самого файла foo.js. В то же время мы также видим, что обновленное значение bar также может быть получено через 500 миллисекунд, что также показывает, что интерфейс, выводимый командой экспорта, и соответствующее ему значение динамически связаны. Этот дизайн позволяет программе определять зависимости модуля во время компиляции, что является самым большим отличием от спецификации модуля CommonJS. Еще один момент, который следует отметить, заключается в том, что, поскольку импорт выполняется статически, импорт имеет эффект подъема, то есть расположение команды импорта не влияет на вывод программы.

После того, как мы разберемся с механизмом загрузки модулей ES6, давайте посмотрим, как ES6 обрабатывает циклические зависимости. Измените приведенный выше пример на:

foo.js

console.log('foo is running');
import {bar} from './bar'
console.log('bar = %j', bar);
setTimeout(() => console.log('bar = %j after 500 ms', bar), 500);
export let foo = false;
console.log('foo is finished');

bar.js

console.log('bar is running');
import {foo} from './foo';
console.log('foo = %j', foo)
export let bar = false;
setTimeout(() => bar = true, 500);
console.log('bar is finished');

воплощать в жизньnode foo.jsвыведет следующее:

bar is running
foo = undefined
bar is finished
foo is running
bar = false
foo is finished
bar = true after 500 ms

foo.js и bar.js образуют циклическую зависимость, но программа выполняется нормально, не попадая в циклический вызов и не сообщая об ошибке. Почему это так? Или потому, что импорт выполняется на этапе компиляции, чтобы программа могла определить зависимости модулей во время компиляции.Как только циклическая зависимость будет найдена, ES6 сам больше не будет выполнять зависимый модуль, поэтому программа может завершиться нормально. Это также показывает, что сам ES6 поддерживает циклические зависимости, гарантируя, что программа не попадет в бесконечные вызовы из-за циклических зависимостей. Тем не менее, мы по-прежнему стараемся избегать циклических зависимостей в наших программах, потому что могут возникать ситуации, которые могут вас смутить. Обратите внимание на вывод выше, вывод в bar.jsfoo = undefined, если вы не заметили, что круговая зависимость заставит вас думать, что это явно в foo.jsexport foo = false, почему это в bar.jsundefinedНу, это путаница, вызванная циклическими зависимостями. В некоторых сложных и масштабных проектах невооруженным глазом сложно найти круговые зависимости, что принесет большие трудности при устранении неполадок. Для проектов, которые используют веб-пакет для сборки проекта, рекомендуется использовать плагин веб-пакета.circular-dependency-pluginЧтобы помочь вам обнаружить все циклические зависимости, существующие в вашем проекте, раннее обнаружение потенциальных циклических зависимостей может избавить вас от многих проблем в будущем.

резюме

Сказав так много, я надеюсь, что эта статья поможет вам лучше понять управление зависимостями в JavaScript и иметь дело с циклическими зависимостями в проектах.