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__).
Эта небольшая разница может привести к тому, что результат выполнения будет отличаться от того, что вы думаете при выполнении кода, который ссылается друг на друга.Представьте себе следующий процесс выполнения кода:
- Установите ключ index.js в объект InstallModules, загрузите index.js и выполните
- столкнуться с импортом a.js
- Установите ключ a.js в объекте createdModules, загрузите a.js и выполните
- встретить импорт index.js
- Проверьте и обнаружите, что ключ index.js уже существует в установленных модулях, и непосредственно прочитайте кэшированный экспорт объекта (на самом деле имя атрибута может быть объявлено только в экспорте, а присвоение отсутствует)
- Выполнить функцию _console при экспорте (возникает ошибка, если атрибуту не присвоено значение)
Способ экспорта повлияет на шаги 5 и 6 описанного выше процесса.
Как решить
- Разорвите замкнутый цикл зависимостей между файлами
- В случае замкнутых зависимостей используйте только функцию экспорта funcName(){}