Webpack — самый популярный интерфейсный пакетный инструмент, который упаковывает код разработки и выводит код, который можно запускать в различных браузерах, повышая эффективность процесса от разработки до выпуска.
Как вы, возможно, уже знаете, это удобство обеспечивается системой плагинов Webpack, но сегодня мы отложим эти концепции в сторону и начнем с простой практики, чтобы изучить, как код, упакованный Webpack, работает в среде браузера.
Простая конфигурация
Файл конфигурации является ключом к использованию Webpack.Файл конфигурации в основном состоит из нескольких частей, таких как запись, вывод, режим, загрузчик и плагин, но если вам нужно только организовать файлы JS, укажите запись и вывод пути к файлу для завершения упаковка мини проекта:
Каталог проекта:
- build
- webpack.config.js — хранит объект конфигурации webpack
- src
- index.js -- исходный файл
- package.json — в этой статье в качестве примера используется webpack ^4.23.0.
Чтобы лучше наблюдать за выходным файлом, мы устанавливаем режим наdevelopment
Отключите сжатие кода и снова включитеsource-map
Поддержка отладки исходного кода.
конфигурационный файлbuild/webpack.config.js
:
const path = require('path');
const resolve = relativePath => path.resolve(__dirname, relativePath);
module.exports = {
mode: 'development',
devtool: 'source-map',
entry: resolve('../src/index.js'),
output: {
path: resolve('../dist'),
}
};
Исходный файлsrc/index.js
:
document.writeln('Hello webpack!');
Теперь запускаем командуwebpack --config build/webpack.config.js
, после упаковки будет дополнительный выходной каталог dist:
- build
- webpack.config.js
-
dist
- main.js
- src
- index.js
- package.json
main
— это имя выходного файла по умолчанию для веб-пакета, давайте быстро взглянем на этот файл:
dist/main.js
:
(function(modules){
// ...
})({
"./src/index.js": (function(){
// ...
})
});
Весь файл содержит только одинФункция немедленного выполнения (IIFE), мы называем егоwebpackBootstrap
, который получает только один объект - незагруженныйКоллекция модулей (модулей), ключ этого объекта модуля — это путь, а значение — функция. Вы спросите, а при чем тут модуль? Как они загружаются?
модуль
Не волнуйтесь, прежде чем мы более подробно рассмотрим выходной код, давайте обогатим исходный код:
Каталог проекта:
- build
- webpack.config.js
- src
-
utils
- math.js
- index.js
-
utils
- package.json
новый файлsrc/utils/math.js
:
export const plus = (a, b) => {
return a + b;
};
export const minus = (a, b) => {
return a - b;
};
src/index.js
:
import {plus, minus} from './utils/math.js';
document.writeln('Hello webpack!');
document.writeln('1 + 2: ', plus(1, 2));
document.writeln('1 - 2: ', minus(1, 2));
Мы написали простой модуль, следуя модульному синтаксису спецификации ES.src/utils/math.js
,Датьsrc/index.js
Цитировать. В настоящее время, хотя основные браузеры начали поддерживать<script type="module">
способ поддержки модуля ES6, новремя покрыть. Webpack по-своему поддерживает спецификацию модуля ES6.Вышеупомянутый модуль является концепцией, соответствующей модулю ES6.
Далее давайте посмотрим, как эти модули реализованы в коде ES5. запустите команду еще разwebpack --config build/webpack.config.js
После просмотра выходного файла:
dist/main.js
:
(function(modules){
// ...
})({
"./src/index.js": (function(){
// ...
}),
"./src/utils/math.js": (function() {
// ...
})
});
В объекте модуля, переданном IIFE, есть дополнительная пара ключ-значение, соответствующая новому модулю.src/utils/math.js
, который перекликается с модулями, которые мы разделили в исходном коде. Однако наличие модулей — это только первый шаг, и конечным результатом этого документа должно быть выполнение каждого модуля в порядке, установленном разработчиком.
Изучите веб-пакетBootstrap
см. далееwebpackBootstrap
Что в функции:
// webpackBootstrap
(function(modules){
// 缓存 __webpack_require__ 函数加载过的模块
var installedModules = {};
/**
* Webpack 加载函数,用来加载 webpack 定义的模块
* @param {String} moduleId 模块 ID,一般为模
块的源码路径,如 "./src/index.js"
* @returns {Object} exports 导出对象
*/
function __webpack_require__(moduleId) {
// ...
}
// 在 __webpack_require__ 函数对象上挂载一些变量
// 及函数 ...
// 传入表达式的值为 "./src/index.js"
return __webpack_require__(__webpack_require__.s = "./src/index.js");
})(/* modules */);
Видно, что на самом деле есть две основные вещи:
- Определить функцию загрузки модуля
__webpack_require__
. - Загрузите входной модуль с помощью функции загрузки
"./src/index.js"
.
весьwebpackBootstrap
В окне отображается только тень входного модуля, а как загружаются другие модули? мы следуем__webpack_require__("./src/index.js")
Присмотритесь к внутренней логике функции загрузки:
// ...
function __webpack_require__(moduleId) {
// 重复加载则利用缓存
if (installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// 如果是第一次加载,则初始化模块对象,并缓存
var module = installedModules[moduleId] = {
i: moduleId, // 模块 ID
l: false, // 模块加载标识
exports: {} // 模块导出对象
};
/**
* 执行模块
* @param module.exports -- 模块导出对象引用,改变模块包裹函数内部的 this 指向
* @param module -- 当前模块对象引用
* @param module.exports -- 模块导出对象引用
* @param __webpack_require__ -- 用于在模块中加载其他模块
*/
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// 模块加载标识置为已加载
module.l = true;
// 返回当前模块的导出对象引用
return module.exports;
}
// ...
Во-первых, функция загрузки использует переменную закрытияinstalledModules
, используемый для хранения загруженных модулей в памяти.
Следующим шагом является инициализация объекта модуля и его монтирование в кеш. Затем идет процесс выполнения модуля, при загрузке входного файлаmodules[moduleId]
На самом деле это./src/index.js
Соответствующая функция модуля. Перед выполнением функции модуля передаются несколько фактических параметров, связанных с модулем, чтобы модуль мог экспортировать содержимое и загружать экспорт других модулей. Наконец, отмечается, что модуль загружен, и возвращается экспортированное содержимое модуля.
согласно с__webpack_require__
Логика кэширования и экспорта, мы знаем, что в течение всего рабочего процесса IIFE, когда кэшированный модуль загружается, он будет напрямую возвращатьсяinstalledModules[moduleId].exports
, другими словами, один и тот же модуль будет выполнять сам модуль только при первой ссылке на него.
функция выполнения модуля
__webpack_require__
прошедшийmodules[moduleId].call()
После запуска функции выполнения модуля мы введемwebpackBootstrap
В разделе параметров посмотрите на функцию выполнения модуля.
// webpackBootstrap
(function(modules){
// ...
})({
/*** 入口模块 ./src/index.js ***/
"./src/index.js": (function (module, __webpack_exports__, __webpack_require__) {
"use strict";
// 用于区分 ES 模块和其他模块规范,不影响理解 demo,战略跳过。
__webpack_require__.r(__webpack_exports__);
// 源模块代码中,`import {plus, minus} from './utils/math.js';` 语句被 loader 解析转化。
// 加载 "./src/utils/math.js" 模块,
/* harmony import */ var _utils_math_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utils/math.js */ "./src/utils/math.js");
document.writeln('Hello webpack!');
document.writeln('1 + 2: ', Object(_utils_math_js__WEBPACK_IMPORTED_MODULE_0__["plus"])(1, 2));
document.writeln('1 - 2: ', Object(_utils_math_js__WEBPACK_IMPORTED_MODULE_0__["minus"])(1, 2));
}),
/*** 工具模块 ./src/utils/math.js ***/
"./src/utils/math.js": (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
// 同 "./src/index.js"
__webpack_require__.r(__webpack_exports__);
// 源模块代码中,`export` 语句被 loader 解析转化。
// 导出 __webpack_exports__
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "plus", function() { return plus; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "minus", function() { return minus; });
const plus = (a, b) => {
return a + b;
};
const minus = (a, b) => {
return a - b;
};
})
});
Порядок выполнения следующий: входной модуль -> инструментальный модуль -> входной модуль. В входном модуле первый проход__webpack_require__("./src/utils/math.js")
получил инструментальный модульexports
объект. Снова взглянув на модуль инструмента, синтаксис экспорта ES преобразуется в__webpack_require__.d(__webpack_exports__, [key], [getter])
,а также__webpack_require__.d
Функция определена вwebpackBootstrap
Внутри:
// ...
// 定义 exports 对象导出的属性。
__webpack_require__.d = function (exports, name, getter) {
// 如果 exports (不含原型链上)没有 [name] 属性,定义该属性的 getter。
if (!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, {
enumerable: true,
get: getter
});
}
};
// 包装 Object.prototype.hasOwnProperty 函数。
__webpack_require__.o = function (object, property) {
return Object.prototype.hasOwnProperty.call(object, property);
};
// ...
видимый__webpack_require__.d
На самом деле этоObject.defineProperty
Простая оболочка для (не зря она называется d ).
Подводя итог,__webpack_exports__
изначально в__webpack_require__
Создан в , начальное значение равно{}
. Этот объект экспорта передается в модуль инструментаmath.js
, добавляется кplus
а такжеminus
, а затем в__webpack_require__
Наконец, экспортируется функция, которая является входным модулем.index.js
используется исполнительной функцией.
Срок службы экспорта:
После обращения к переменным, экспортированным модулем инструмента, модуль ввода выполняет оставшуюся часть. На этом базовый процесс выполнения модуля Webpack завершен.
Вышеупомянутое может бытьКлонировать образец кода для отладки, ветка естьdemo1
.
В дополнение к спецификации модуля ES6, Webpack также поддерживает спецификации CommonJS и AMD, вы можете заменить модульную спецификацию и переупаковать, чтобы увидеть разницу.
резюме
Что ж, давайте суммируем идеи загрузки модулей Webpack с помощью блок-схемы: