[Расширенный веб-пакет] Модульный дизайн и реализация клиентской среды выполнения

Node.js внешний интерфейс JavaScript браузер Webpack
[Расширенный веб-пакет] Модульный дизайн и реализация клиентской среды выполнения

Вы действительно понимаете модульность интерфейса?

Попрощайтесь с «инженером по настройке веб-пакета»

webpack — это мощный и сложный интерфейсный инструмент автоматизации. Одной из особенностей является то, что конфигурация сложная, что также делает популярным шутливое название «инженер по настройке веб-пакета»🤷 Однако вас действительно устраивает только игра с конфигурацией веб-пакета?

Очевидно нет. В дополнение к изучению того, как использовать веб-пакет, нам нужно углубиться в веб-пакет и изучить дизайн и реализацию каждой части. Даже если однажды веб-пакет «устарел», некоторые из его проектов и реализаций по-прежнему будут иметь ценность для изучения и справочное значение. Поэтому в процессе изучения webpack я подытожу серию статей [webpack advanced] и поделюсь ими с вами.

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

1. Введение

Введите тему ниже. В течение долгого времени в области фронтенда растущий спрос на языковые способности разработчиков и отсталая спецификация JavaScript сформировали большое противоречие. Например, мы будем использовать babel для преобразования синтаксиса ES6 в ES5, и мы будем использовать различные полифилы для размещения старомодных новых функций... и главного героя нашей статьи — модуляризации.

Поскольку JavaScript не учитывал это в начале своего проектирования, в сочетании с поздним появлением модульной спецификации, в сообществе появился ряд модульных решений для клиентской среды выполнения, таких как RequireJS, seaJS и т. д. И соответствующие решения для зависимостей модулей времени компиляции, такие как browserify, rollup и webpack, главный герой этой статьи.

Но нам нужно знать,<script type="module">Есть также некоторые проблемы совместимости и использования.

В более общем смысле браузеры изначально не поддерживают так называемые спецификации модульности CommonJS или ESM. Так как же Webpack реализует модульность в упакованном коде?

2. Модульность в NodeJS

Прежде чем исследовать модульную реализацию упакованного кода webpack, давайте взглянем на модульность в Node.

NodeJS (далее Node) в основном следует спецификации CommonJS с точки зрения модульности, и модульность кода, упакованного webpack, также аналогична CommonJS. Поэтому давайте возьмем знакомый Node (здесь в основном ссылаемся на Node v10) в качестве введения, кратко представим его модульную реализацию, а затем поможем нам понять реализацию webpack.

Внедрение модуля в Node проходит через следующие этапы:

  1. анализ пути
  2. местоположение файла
  3. Скомпилировать и выполнить

В Node модули существуют в файловом измерении и кэшируются в памяти после компиляции черезrequire.cacheВы можете просмотреть состояние кэша модуля. добавить в модульconsole.log(require.cache)Просмотрите вывод следующим образом:

{ '/Users/alienzhou/programming/gitrepo/test.js':
   Module {
     id: '.',
     exports: {},
     parent: null,
     filename: '/Users/alienzhou/programming/gitrepo/test.js',
     loaded: false,
     children: [],
     paths:
      [ '/Users/alienzhou/programming/gitrepo/node_modules',
        '/Users/alienzhou/programming/node_modules',
        '/Users/alienzhou/node_modules',
        '/Users/node_modules',
        '/node_modules' ] } }

Выше приведена структура данных объекта модуля, которую также можно найти вИсходный код узлаНайдите конструктор класса Module в . вexportsАтрибут очень важен, это экспортируемый объект модуля. Поэтому следующая строка

var test = require('./test.js');

Фактически,test.jsмодульныйexportsсвойство назначеноtestПеременная.

Возможно, вам все еще любопытно, когда мы пишем модуль Node (JavaScript),module,require,__filenameОткуда берутся эти переменные? если ты виделNode loader.js часть исходного кода, должно быть примерно понятно:

Module.wrap = function(script) {
  return Module.wrapper[0] + script + Module.wrapper[1];
};

Module.wrapper = [
  '(function (exports, require, module, __filename, __dirname) { ',
  '\n});'
];

Node автоматически превращает каждый модуль в функцию. Например, модуль test.js изначально такой:

console.log(require.cache);
module.exports = 'test';

После упаковки это будет примерно:

(function (exports, require, module, __filename, __dirname) {
    console.log(require.cache);
    module.exports = 'test';
});

Теперь вы должны понятьmodule,require,__filenameОткуда берутся эти переменные - они будут внедрены как параметры функции при компиляции и выполнении модуля. с расширением.jsмодуль, например, когда выrequireВ настоящее время полный вызов метода примерно включает следующие процессы:

st=>start: require()引入模块
op1=>operation: 调用._load()加载模块
op2=>operation: new Module(filename, parent)创建模块对象
op3=>operation: 将模块对象存入缓存
op4=>operation: 根据文件类型调用Module._extensions
op5=>operation: 调用.compile()编译执行js模块
cond=>condition: Module._cache是否无缓存
e=>end: 返回module.exports结果
st->op1->cond
cond(yes)->op2->op3->op4->op5->e
cond(no)->e

существуетИсходный код узлаКак видите, при выполнении модуля вводятся несколько переменных, определенных пакетом:

if (inspectorWrapper) {
    result = inspectorWrapper(compiledWrapper, this.exports, this.exports,
                              require, this, filename, dirname);

} else {
    result = compiledWrapper.call(this.exports, this.exports, require, this,
                                  filename, dirname);
}

Не по теме, отсюда же видно, что внутри модуля usemodule.exportsа такжеexportsразница

3. Модульность интерфейса, реализованная с помощью webpack

Прежде чем рассказать о том, «как webpack реализует модульность в упакованном коде», причина введения модульности в Node заключается в том, что они имеют сходство в дизайне и реализации синхронных зависимостей. Понимание модульности Node полезно для изучения webpack. Конечно, из-за разных операционных сред (код, упакованный webpack, работает на стороне клиента, а Node — на стороне сервера), существуют и определенные различия в реализации.

Давайте посмотрим, как webpack реализует модульность внешнего интерфейса (на стороне клиента) в упакованном коде.

3.1 Объекты модуля

Подобно модульной реализации Node, в коде, упакованном webpack, каждый модуль также имеет соответствующий объект модуля. существует__webpack_require__()В методе есть такой кусок кода:

function __webpack_require__(moduleId) {
    // …… other code
    
    var module = installedModules[moduleId] = {
        i: moduleId,
        l: false,
        exports: {},
        parents: null,
        children: []
    };
    
    // …… other code
}

Подобно Node, каждый модуль в webpack также имеет соответствующий объект модуля, и его структура данных в основном соответствует спецификации CommonJS;installedModulesЭто объект кеша модуля, аналогичный объекту в Node.require.cache/Module._cache.

2.2. Модуль требует:__webpack_require__

__webpack_require__Это очень важный метод модуляции среды выполнения веб-интерфейса, который эквивалентен спецификации CommonJS.require.

Согласно блок-схеме в Части 1: В Node, когда мыrequireКогда модуль используется, он сначала определяет, находится ли этот модуль в кеше, и если он существует, он напрямую возвращает значение модуля.exportsАтрибуты; в противном случае модуль загружен и выполняется. Реализация в WebPack также похожа:

function __webpack_require__(moduleId) {
    // 1.首先会检查模块缓存
    if(installedModules[moduleId]) {
        return installedModules[moduleId].exports;
    }
    
    // 2. 缓存不存在时,创建并缓存一个新的模块对象,类似Node中的new Module操作
    var module = installedModules[moduleId] = {
        i: moduleId,
        l: false,
        exports: {},
        children: []
    };

    // 3. 执行模块,类似于Node中的:
    // result = compiledWrapper.call(this.exports, this.exports, require, this, filename, dirname);
    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

    module.l = true;

    // 4. 返回该module的输出
    return module.exports;
}

Если вы внимательно сравните webpack и Node, вы обнаружите, что в__webpack_require__Есть важное отличие:

Вызов как Node не существует в веб-пакете._compile()процесс этого метода. То есть, в отличие от Node, для модуля, который не загружается в кеш, модуль загружается путем «чтения пути модуля -> компиляция кода модуля -> выполнение модуля». Зачем?

Это связано с тем, что Node, как серверный язык, все модули являются локальными файлами с низкой задержкой загрузки и могут синхронно блокироваться для адресации, чтения, компиляции и выполнения файлов модулей.Эти процессы выполняются «по требованию», когда модули Этого достаточно; хотя веб-пакет работает на стороне клиента (браузера), очевидно, что он не может быть выполнен, когда это необходимо (т.__webpack_require__time), а затем загрузите файл js через сеть и синхронно дождитесь завершения загрузки, прежде чем вернуться__webpack_require__. Такая сетевая задержка явно не может удовлетворить требованию «зависимости от синхронизации».

Так как же Webpack решает эту проблему?

3.2. Как решить внешние зависимости синхронизации

Вернемся и посмотрим на Node:

Код для загрузки, компиляции и выполнения (js) модулей в Node (v10) в основном сосредоточен вModule._extensions['.js']а такжеModule.prototype._compileсередина. сначала пройдетfs.readFileSyncПрочитайте содержимое файла, затем передайтеvm.runInThisContextдля компиляции и выполнения кода JavaScript.

The vm module provides APIs for compiling and running code within V8 Virtual Machine contexts.

Однако, согласно приведенному выше анализу, определенно невозможно синхронно получать файлы JavaScript-скриптов через сеть во время выполнения внешнего интерфейса; тогда нам нужно изменить свое мышление: есть ли место для предварительного размещения модулей, которые мы можем нужно «позже», чтобы мы могли Не нужно долго ждать синхронно при запросе (конечно, «после» здесь может быть несколько секунд, несколько минут или следующие несколько строк кода в этом задача цикла событий).

Память - хороший выбор. Мы можем сначала «зарегистрировать» модули, которые зависят от синхронизации, в памяти (временное хранилище модулей), а затем выполнить модуль, кэшировать объект модуля и вернуть соответствующий модуль, когда он потребуется.exports. В webpack эта так называемая памятьmodulesобъект.

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

Итак, процесс происходит примерно так:

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

3.3. Как «поставить» модуль

Идея ясна, так что давайте посмотрим, как webpack «временно хранит» модули вmodulesна объекте. На самом деле код, упакованный webpack, можно просто разделить на две категории:

  • Одна из них — интерфейсная среда выполнения, модульная с помощью веб-пакета.Вы можете просто сравнить функции, реализованные интерфейсными модульными библиотеками, такими как RequireJS. Управляет загрузкой, кэшированием модулей, предоставляет такие функции, как__webpack_require__Такой метод require и т.д.
  • Другой тип — это код для регистрации и работы модуля, который включает код модуля в исходный код. Для дальнейшего понимания давайте сначала посмотрим, как выглядит эта часть кода.

Для облегчения обучения и чтения кода рекомендуется добавить его в конфигурацию webpack (v4).optimization:{runtimeChunk: {name: 'runtime'}}, что позволит webpack упаковать среду выполнения отдельно от регистрационного кода модуля.

// webpack module chunk
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["home-0"],{

/***/ "module-home-0":
/***/ (function(module, exports, __webpack_require__) {

const myalert = __webpack_require__("module-home-1");

myalert('test');

/***/ }),

/***/ "module-home-1":
/***/ (function(module, exports) {

module.exports = function (a) {
    alert('hi:' + a);
};

/***/ })

},[["module-home-0","home-1"]]]);

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

// webpack module chunk
window["webpackJsonp"].push([
    ["home-0"], // chunkIds
    {
        "module-home-0": (function(module, exports, __webpack_require__){ /* some logic */ }),
        "module-home-1": (function(module, exports, __webpack_require__){ /* some logic */ })
    },
    [["module-home-0","home-1"]]
])

здесь,.push()Параметр метода представляет собой массив из трех элементов:

  • Первый элемент представляет собой массив,["home-0"]Указывает идентификаторы всех чанков, содержащихся в файле js (которые можно примерно понимать как модули в форме чанков веб-пакета, а чанки формируют файлы);
  • Второй элемент — это объект, ключ — это идентификатор каждого модуля, а значение — это модуль, обернутый функцией;
  • Третий элемент также является массивом, который, в свою очередь, состоит из нескольких массивов. Что касается конкретной роли, давайте сначала нажмем на стол, а потом поговорим об этом в конце.

Взгляните на второй элемент массива параметров — объект, содержащий код модуля, и вы увидите, что здесь сигнатура метода очень похожа на сигнатуру в Node.Module.wrap()Делать перенос кода модуля? Вот так,исходный код веб-пакетаНечто подобное есть и в Node, который оборачивает код каждого модуля функцией вроде Node.

Когда веб-пакет настроен с разделением среды выполнения, в упакованном файле появится «чистая» среда выполнения без какого-либо кода модуля, которая в основном представляет собой самовыполняющийся метод, который предоставляет глобальную переменную.webpackJsonp:

// webpack runtime chunk
var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
jsonpArray.push = webpackJsonpCallback;

webpackJsonpимена переменных могут быть переданыoutput.jsonpFunctionнастроить

можно увидеть,window["webpackJsonp"]Вверх.push()Метод был модифицирован дляwebpackJsonpCallback()метод. Метод заключается в следующем:

// webpack runtime chunk
function webpackJsonpCallback(data) {
    var chunkIds = data[0];
    var moreModules = data[1];
    var executeModules = data[2];

    var moduleId, chunkId, i = 0, resolves = [];
    // webpack会在installChunks中存储chunk的载入状态,据此判断chunk是否加载完毕
    for(;i < chunkIds.length; i++) {
        chunkId = chunkIds[i];
        if(installedChunks[chunkId]) {
            resolves.push(installedChunks[chunkId][0]);
        }
        installedChunks[chunkId] = 0;
    }
    
    // 注意,这里会进行“注册”,将模块暂存入内存中
    // 将module chunk中第二个数组元素包含的 module 方法注册到 modules 对象里
    for(moduleId in moreModules) {
        if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
            modules[moduleId] = moreModules[moduleId];
        }
    }

    if(parentJsonpFunction) parentJsonpFunction(data);

    while(resolves.length) {
        resolves.shift()();
    }

    deferredModules.push.apply(deferredModules, executeModules || []);

    return checkDeferredModules();
};

Обратите внимание, что эти строки вышеприведенного метода — это то, что мы говорили ранее о «постановке» модуля на объект модулей.

// webpackJsonpCallback
for(moduleId in moreModules) {
    if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
    modules[moduleId] = moreModules[moduleId];
    }
}

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

// __webpack_require__
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

3.4 Автоматическое выполнение модулей

Пока внедрена реализация синхронной зависимости webpack, но есть еще небольшая проблема: все исходники js в webpack являются модулями, но если это модули, которые не будут выполняться автоматически, то мы как раз во фронтенде Внесена куча "мертвого" кода, как сделать код "живым"?

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

Как webpack заставляет эти модули ввода выполняться автоматически? Я не знаю, помните ли вы третий параметр в фрагменте модуля, которого нет в списке: этот параметр является массивом, и каждый элемент массива является массивом.

[["module-home-0","home-1"], ["module-home-2","home-3","home-5"]]

В отличие от приведенного выше примера, мы можем подробно объяснить значение параметров. первый элемент["module-home-0","home-1"]означает, что я хочу автоматизировать moduleId какmodule-home-0этот модуль, но модуль требует, чтобы chunkId былhome-1Чанк может быть выполнен после того, как он был загружен; аналогично,["module-home-2","home-3","home-5"]Указывает на автоматическое выполнениеmodule-home-2модули, но нужно проверять кускиhome-3а такжеhome-5уже загружен.

Выполнение некоторых модулей должно гарантировать загрузку некоторых чанков, потому что другие модули, от которых зависит модуль, могут отсутствовать в текущем чанке, и webpack автоматически вставит в него chunkId зависимого модуля посредством анализа зависимостей во время компиляции.

Функции, «автоматически» выполняемые этим модулем, в основном находятся в коде исполняемого фрагментаcheckDeferredModules()Реализация метода:

function checkDeferredModules() {
    var result;
    for(var i = 0; i < deferredModules.length; i++) {
        var deferredModule = deferredModules[i];
        var fulfilled = true;
        // 第一个元素是模块id,后面是其所需的chunk
        for(var j = 1; j < deferredModule.length; j++) {
            var depId = deferredModule[j];
            // 这里会首先判断模块所需chunk是否已经加载完毕
            if(installedChunks[depId] !== 0) fulfilled = false;
        }
        // 只有模块所需的chunk都加载完毕,该模块才会被执行(__webpack_require__)
        if(fulfilled) {
            deferredModules.splice(i--, 1);
            result = __webpack_require__(__webpack_require__.s = deferredModule[0]);
        }
    }
    return result;
}

4. Асинхронные зависимости

Если вы просто хотите изучить дизайн и реализацию зависимостей синхронизации во внешнем интерфейсе выполнения веб-пакета, основной контент в основном здесь. Однако мы знаем, что webpack поддерживает синтаксис (разделение кода), представленный с помощью динамических модулей, например:dynamic importи раноrequire.ensure, этот способ аналогичен использованию CommonJSrequireи ESMimportСамое важное отличие состоит в том, что этот метод класса загружает зависимости асинхронно (или по требованию).

4.1 Преобразование кода

как использование в исходном кодеrequireбудет заменен веб-пакетом при сборке__webpack_require__Точно так же синтаксис асинхронной зависимости, используемый в исходном коде, изменяется webpack. кdynamic importНапример, следующий код

import('./test.js').then(mod => {
    console.log(mod);
});

будет преобразован в

__webpack_require__.e(/* import() */ "home-1")
    .then(__webpack_require__.bind(null, "module-home-3"))
    .then(mod => {
        console.log(mod);
    });

Что означает приведенный выше код? Мы знаем, что после того, как webpack будет упакован, некоторые модули будут объединены в чанк, поэтому приведенное выше"home-1"означает: содержит./test.jsЧанкИд чанка модуля"home-1".

первый проход веб-пакета__webpack_require__.eЗагрузите файл скрипта (фрагмент модуля) указанного фрагмента, этот метод возвращает обещание и разрешает обещание, когда скрипт загружается и выполняется. При связывании веб-пакет гарантирует, что все модули, которые асинхронно зависят от него, включены в фрагмент модуля или текущий контекст.

Теперь, когда фрагмент модуля выполнен, это указывает на то, что асинхронная зависимость готова, поэтому она выполняется в методе then.__webpack_require__Цитироватьtest.jsModule (moduleId — это module-home-3 после компиляции веб-пакета) и return. Таким образом, модуль может нормально использоваться во втором методе.

4.2. __webpack_require__.e

Основным методом асинхронных зависимостей является__webpack_require__.e. Проанализируем этот метод:

__webpack_require__.e = function requireEnsure(chunkId) {
    var promises = [];
    var installedChunkData = installedChunks[chunkId];
    
    // 判断该chunk是否已经被加载,0表示已加载。installChunk中的状态:
    // undefined:chunk未进行加载,
    // null:chunk preloaded/prefetched
    // Promise:chunk正在加载中
    // 0:chunk加载完毕
    if(installedChunkData !== 0) {
        // chunk不为null和undefined,则为Promise,表示加载中,继续等待
        if(installedChunkData) {
            promises.push(installedChunkData[2]);
        } else {
            // 注意这里installChunk的数据格式
            // 从左到右三个元素分别为resolve、reject、promise
            var promise = new Promise(function(resolve, reject) {
                installedChunkData = installedChunks[chunkId] = [resolve, reject];
            });
            promises.push(installedChunkData[2] = promise);

            // 下面代码主要是根据chunkId加载对应的script脚本
            var head = document.getElementsByTagName('head')[0];
            var script = document.createElement('script');
            var onScriptComplete;

            script.charset = 'utf-8';
            script.timeout = 120;
            if (__webpack_require__.nc) {
                script.setAttribute("nonce", __webpack_require__.nc);
            }
            
            // jsonpScriptSrc方法会根据传入的chunkId返回对应的文件路径
            script.src = jsonpScriptSrc(chunkId);

            onScriptComplete = function (event) {
                script.onerror = script.onload = null;
                clearTimeout(timeout);
                var chunk = installedChunks[chunkId];
                if(chunk !== 0) {
                    if(chunk) {
                        var errorType = event && (event.type === 'load' ? 'missing' : event.type);
                        var realSrc = event && event.target && event.target.src;
                        var error = new Error('Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')');
                        error.type = errorType;
                        error.request = realSrc;
                        chunk[1](error);
                    }
                    installedChunks[chunkId] = undefined;
                }
            };
            var timeout = setTimeout(function(){
                onScriptComplete({ type: 'timeout', target: script });
            }, 120000);
            script.onerror = script.onload = onScriptComplete;
            head.appendChild(script);
        }
    }
    return Promise.all(promises);
};

Этот метод сначала оценивает, загружается ли чанк или уже был загружен в соответствии с chunkId в installChunks; если нет, он создаст промис, сохранит его в installChunks и передастjsonpScriptSrc()Метод получает путь к файлу, загружает его через тег сценария и, наконец, возвращает обещание.

jsonpScriptSrc()Его можно понимать как метод, содержащий карту фрагментов, например, в этом примере:

function jsonpScriptSrc(chunkId) {
    return __webpack_require__.p + "" + ({}[chunkId]||chunkId) + "." + {"home-1":"0b49ae3b"}[chunkId] + ".js"
}

который содержит карту -{"home-1":"0b49ae3b"}, вернет имя файла home-1.0b49ae3b.js в соответствии с chunkId home-1.

4.3 Обновить статус загрузки чанка

Наконец, вы обнаружите, что при загрузке метод разрешения обещания не вызывается. Так когда же это разрешилось?

Вы помните, как ввели синхронное требование для регистрации модулей?webpackJsonpCallback()метод? Как мы уже говорили, первый элемент в массиве параметров метода — это массив chunkId, представляющий фрагменты, содержащиеся в скрипте.

p.s. Когда обычный скрипт загружается браузером, сначала будет выполняться скрипт, а затем срабатывает событие onload.

Таким образом, вwebpackJsonpCallback()В методе есть кусок кода, который проверяет и обновляет статус загрузки чанков на основе массива chunkId:

// webpackJsonpCallback()
var moduleId, chunkId, i = 0, resolves = [];
for(;i < chunkIds.length; i++) {
    chunkId = chunkIds[i];
    if(installedChunks[chunkId]) {
        resolves.push(installedChunks[chunkId][0]);
    }
    installedChunks[chunkId] = 0;
}

// ……

while(resolves.length) {
    resolves.shift()();
}

Приведенный выше код сначала извлекает все загруженные фрагменты, соответствующие установленным фрагментам, в соответствии с chunkId, когда модуль зарегистрирован, и, наконец, разрешает промисы этих фрагментов.

5. Пишите в конце

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

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

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

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