Рекомендации Webpack по обработке циклических ссылок и экспорту назначений

внешний интерфейс GitHub JavaScript Webpack

webpack связывает модули, которые ссылаются друг на друга

пример проекта

(github)

возникшие проблемы

Когда есть два или более файла с взаимозависимостями, образующими замкнутый цикл, иногда возникаетCan't read Property 'xxx' of undefinedили(0,xxx) is not a functionОшибки такого типа, такие как:

示例项目中的src/index.js引用src/a.js,而src/a.js中也引用了src/index.js

Почему возникает такая проблема

Это связано с логикой выполнения кода после упаковки webpack.

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

// Check if module is in cache
if(installedModules[moduleId]) {
  return installedModules[moduleId].exports;
}
// Create a new module (and put it into the cache)
var module = installedModules[moduleId] = {
  i: moduleId,
  l: false,
  exports: {}
};
// Execute the module function
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__)

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

Возьмите код в export function.js и код в export const _var.js, например:

export function.js


/***/ (function(module, exports, __webpack_require__) {

  "use strict";
  
  
  Object.defineProperty(exports, "__esModule", {
    value: true
  });
  exports._console = _console;// <- 📢注意这里
  
  var _a = __webpack_require__(2);
  
  var _a2 = _interopRequireDefault(_a);
  
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  
  function _console() {
    console.log('this is index.js');
  }
  
  /***/ }),

export const _var.js


/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports._console = undefined;// <- 📢注意这里

var _a = __webpack_require__(2);

var _a2 = _interopRequireDefault(_a);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var _console = exports._console = function _console() {
  console.log('this is index.js');
};

/***/ }),

Как видно из строки кода в 📢 в двух приведенных выше фрагментах кода, после того, как код, использующий оператор присваивания для экспорта, упакован, присвоение атрибутам экспорта будет после импорта (то есть __webpack_require__), и другой метод, который использует оператор объявления функции для экспорта. После того, как код упакован, назначение атрибутов экспорта будет перед импортом (то есть __webpack_require__).

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

  1. Установите ключ index.js в объект InstallModules, загрузите index.js и выполните
  2. столкнуться с импортом a.js
  3. Установите ключ a.js в объекте createdModules, загрузите a.js и выполните
  4. встретить импорт index.js
  5. Проверьте и обнаружите, что ключ index.js уже существует в установленных модулях, и непосредственно прочитайте кэшированный экспорт объекта (на самом деле имя атрибута может быть объявлено только в экспорте, а присвоение отсутствует)
  6. Выполнить функцию _console при экспорте (возникает ошибка, если атрибуту не присвоено значение)

Способ экспорта повлияет на шаги 5 и 6 описанного выше процесса.

Как решить

  1. Разорвите замкнутый цикл зависимостей между файлами
  2. В случае замкнутых зависимостей используйте только функцию экспорта funcName(){}