С webpack все является модулем. Следовательно, каким бы ни был файл, его необходимо преобразовать в модуль, распознаваемый js. Вы можете понять, что независимо от того, какой суффикс у файла, он используется как js (даже файлы img, ppt, txt и т. д.). Но определенно невозможно использовать его напрямую как js.Его необходимо преобразовать в способ, понятный js, прежде чем его можно будет использовать в качестве js-модуля - этот процесс преобразования обрабатывается загрузчиком веб-пакета. Загрузчик webpack — это js-модуль, экспортируемый как функция. внутренний для веб-пакета
loader runnerЭта функция будет вызвана, а затем передан файл результата или ресурса, сгенерированный предыдущим загрузчиком, а затем возвращен обработанный результат.
Далее мы начнем с базового использования, рассмотрим, как написать загрузчик и реализовать его.raw-loader,json-loader,url-loader,bundle-loader
Подготовка: сначала установитеwebpack,webpack-cli,webpack-dev-server, что будет использоваться в более поздней практике, а затем установлено?
Использование погрузчика
- Традиционный метод: настроить правила в webpack.config.
module.exports = {
module: {
rules: [
{
test: /\.js$/, // 匹配规则
use: ['babel-loader'] // require的loader路径数组
}
]
}
}
Написал это правило, если совпадающее имя файла начинается с.jsВ конце концов, он будет обработан всеми загрузчиками в использовании.
- имя_загрузчика! префикс метод
Например, есть файл txt, который мы хотим передать
raw-loaderчтобы получить строковое содержимое во всем текстовом файле. Помимо использования унифицированной конфигурации конфигурации веб-пакета, мы также можем импортировать ее со следующим синтаксисом:
import txt from "raw-loader!./1.txt";
// txt就是这个文件里面所有的内容
На самом деле, после использования файла webpack.config для унифицированной настройки загрузчика, он в конечном итоге будет преобразован в этот способ использования загрузчика, а затем повторно введен. Поддержка нескольких загрузчиков, синтаксис:loader1!loader2!yourfilename
Параметры замены запросов
Используйте синтаксис префикса loadername!:raw-loader?a=1&b=2!./1.txt, что эквивалентно конфигурации веб-пакета:
{
test: /^1\.txt$/,
exclude: /node_modules/,
use: [
{ loader: "raw-loader", options: { a: '1', b: '2' } },
]
},
При написании собственного загрузчика вы часто используетеloader-utils(специальной установки не требуется, идет в комплекте с установленным вебпаком) для получения входящих параметров
const { getOptions } = require("loader-utils");
module.exports = function(content) {
const options = getOptions(this) || {};
// 如果是配置,返回的是options;如果是loadername!语法,返回根据query字符串生成的对象
// ...
};
Для удобства демонстрации ниже этот метод будет использоваться несколько раз для настройки загрузчика. Если вы еще не использовали этот метод раньше, примите его как введение. вставай~
Как вообще выглядит загрузчик
Загрузчик — это js-модуль, экспортируемый как функция, эта функция имеет три параметра: контент, карта, мета.
- содержимое: представляет строку исходного файла или буфер
- карта: представляет объект исходной карты.
- мета: представляет метаданные, вспомогательный объект
Реализуем один из самых простых и простых, добавив в код консольный загрузчик:
// console.js
module.exports = function(content, map, meta) {
return `${content}; console.log('loader exec')`;
};
конфигурация веб-пакета
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{ loader: "./loaders/console" }, // 加上自己写的loader
]
}
]
},
'loader exec'
Самый простой загрузчик — raw-loader и json-loader
Эти два загрузчика должны прочитать содержимое файла, а затем использовать import или require для импорта всего содержимого исходного файла. Очевидно, что когда исходный файл используется как js, оператор экспорта отсутствует, и загрузчик добавляет оператор экспорта.
Например, есть такой txt
this is a txt file
Если вы используете его как js, при импорте или запросе выполнитеthis is a txt fileЭтот js обязательно сообщит об ошибке. Если вы хотите использовать его в обычном режиме, то этот текстовый файл необходимо изменить на:
export default 'this is a txt file'
Конечным результатом является то, что независимо от того, какой файл txt, md, json и т. д. используется в качестве файла js, содержимое исходного файла эквивалентно строке, которая экспортируется:
// 自己写的raw-loader
const { getOptions } = require("loader-utils");
// 获取webpack配置的options,写loader的固定套路第一步
module.exports = function(content, map, meta) {
const opts = getOptions(this) || {};
const code = JSON.stringify(content);
const isESM = typeof opts.esModule !== "undefined" ? options.esModule : true;
// 直接返回原文件内容
return `${isESM ? "export default" : "module.exports ="} ${code}`;
};
raw-loaderа такжеjson-loaderПрактически одинаковые, их цель экспортировать все содержимое исходного файла в виде строки, а у json-loader есть дополнительный процесс json.parse
Примечание. Я просмотрел официальный исходный код загрузчика и обнаружил, что в них будет еще один шаг.
JSON.stringify(content)
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');
\u2028а также\u2029являются специальными символами и\n,\bПохожие, но они особенные в том, что после экранирования это все еще пустая строка интуитивно. Вы можете увидеть его особенности:
Даже если вы видите странный символ посередине, но снова нажимаете ввод, все равно'ab',\u2028Строки интуитивно эквивалентны пустым строкам (на самом деле символы есть, но не действуют). А за исключением 2028 и 2029 вроде\u000Aиз\n, который имеет эффект разрыва строки (символ существует, и он также имеет эффект, который он приносит). Поэтому необходимо экранировать значения символов 2028 и 2029 с малой вероятностью появления
| Значение символа Юникода | escape-последовательность | имея в виду | категория |
|---|---|---|---|
| \u0008 | \b | Backspace | |
| \u0009 | \t | Tab | пустой |
| \u000A | \n | новая строка (новая строка) | терминатор строки |
| \u000B | \v | вертикальная вкладка | пустой |
| \u000C | \f | подача формы | пустой |
| \u000D | \r | Войти | терминатор строки |
| \u0022 | " | Двойные кавычки (") | |
| \u0027 | \' | апостроф (') | |
| \u005C | \ | обратная косая черта () | |
| \u00A0 | неразрывный пробел | пустой | |
| \u2028 | разделитель строк | терминатор строки | |
| \u2029 | разделитель абзаца | терминатор строки | |
| \uFEFF | знак порядка байтов | пустой |
необработанный режим и URL-загрузчик
Мы уже достиглиraw-loader, этот загрузчик возвращает содержимое исходного файла в виде строки. Но вот проблема: некоторые файлы не могут быть решены одной строкой, например изображения, видео и аудио. На этом этапе нам нужно напрямую использоватьbuffer. Именно так, первый параметр функции загрузчика, content, поддерживаетstring/buffer
Как открыть содержимое буферного типа?
// 只需要导出raw为true
module.exports.raw = true
url-loaderПроцесс такой, прочитать конфигурацию, можно ли ее перенести, как передать => прочитать исходный файл буфера => буфер для вывода base64 => не может преобразовать резервный процесс. Реализуем упрощенную версию нижеurl-loader, который реализует только основные функции
const { getOptions } = require("loader-utils");
module.exports = function(content) {
const options = getOptions(this) || {};
const mimetype = options.mimetype;
const esModule =
typeof options.esModule !== "undefined" ? options.esModule : true;
// base编码组成:data:[mime类型];base64,[文件编码后内容]
return `${esModule ? "export default" : "module.exports ="} ${JSON.stringify(
`data:${mimetype || ""};base64,${content.toString("base64")}`
)}`;
};
module.exports.raw = true;
Затем давайте получим случайное изображение и импортируем его, чтобы попробовать:
// loader路径自行修改
// img就是一个base64的图片路径,可以直接放img标签使用
import img from "../../loaders/my-url-loader?mimetype=image!./1.png";
Что касаетсяfile-loaderЯ считаю, что у каждого есть идея, процесс: прочитайте Production Pathion в конфигурации => Определите окончательный выходной путь => Имя файла Plus HASH HASH => Переместить файл, изменить имя файла на новое имя => новый Имя файла сращивание предыдущего пути => Вывод окончательного пути файла
загрузчик смолы и пачки
Официальный сайт представляет качковый погрузчикДа: Загрузчики всегда вызываются справа налево. В некоторых случаях загрузчик заботится только о метаданных после запроса и игнорирует результаты предыдущего загрузчика. Метод pitch в загрузчике вызывается слева направо перед фактическим (справа налево) выполнением загрузчика.Во-вторых, если загрузчик возвращает результат в методе питча, процесс пропускает остальные загрузчики.
Три параметра метода основного тона:
- оставшийся запрос: следующий путь к загрузчику + ресурсу, синтаксис имени загрузчика!
- PreviousRequest: путь к ресурсу
- метаданные: То же, что и третий параметр обычной функции загрузчика, вспомогательный объект, и этот же объект используется для всего процесса выполнения загрузчика.
Загрузчик выполняет этот процесс от конца к началу, и вы можете рассматривать его как последовательное нажатие и выталкивание в обратном порядке. Например, файл, попадающий под определенное правило A, пройдет через 3 загрузчика:['a-loader', 'b-loader', 'c-loader']
пройдет через этот процесс:
- Выполнить загрузчик
pitchметод - выполнить b-загрузчик
pitchметод - выполнить c-загрузчик
pitchметод - Получить содержимое ресурса на основе пути импорта/требования
- выполнение c-загрузчика
- выполнение b-загрузчика
- выполнение загрузчика
еслиb-loaderВ нем есть метод питча, и этот метод питча имеет возвращаемый результат, значит вышеописанный процесс прошел с тех порb-loaderПосле этого уже не будетc-loaderвставить в стек
// b-loader
module.exports = function(content) {
return content;
};
// 没做什么,就透传import进来再export出去
module.exports.pitch = function(remainingRequest) {
// remainingRequest路径要加-! 前缀
return `import s from ${JSON.stringify(
`-!${remainingRequest}`
)}; export default s`;
};
Метод питча b-loader имеет возвращаемый результат и будет проходить через следующий процесс:
- Выполнить загрузчик
pitchметод - выполнить b-загрузчик
pitchметод (с возвращаемым результатом, пропустить c-loader) - Получить содержимое ресурса на основе пути импорта/требования
- выполнение b-загрузчика
- выполнение загрузчика
При каких обстоятельствах вам нужно пропустить остальную часть загрузчика? Чаще всего используется динамическая загрузка и чтение кэша, а расчет последующего загрузчика следует пропустить.
bundle-loaderтипичный пример
bundle-loaderЧто достигается динамической загрузкой по требованию, как ее использовать? Мы можем преобразовать последний шаг реакции ReactDom.render и заменить его динамической загрузкой.react-dom, а затем почувствуйте разницу
- import ReactDom from "react-dom";
+ import LazyReactDom from "bundle-loader?lazy&name=reactDom!react-dom";
+ LazyReactDom(ReactDom => {
+ console.log(ReactDom, "ReactDom");
ReactDom.render(<S />, document.getElementById("root"));
+});
Реактдом можно рассматривать как изолированный, введенный в динамику
нажмите наbundle-loaderисходный код, обнаружил, что он используетrequire.ensureЧтобы динамически представить, конкретная реализация также очень проста, см.исходный код загрузчика пакетов. Времена меняются, и динамичное введение новой эры должно动态import, реализуем новый на основе динамического импортаbundle-loader. (Реализует только основные функции, введенные ленивым)
// 获取ChunkName
function getChunkNameFromRemainingRequest(r) {
const paths = r.split("/");
let cursor = paths.length - 1;
if (/^index\./.test(paths[cursor])) {
cursor--;
}
return paths[cursor];
}
// 原loader不需要做什么了
module.exports = function() {};
module.exports.pitch = function(remainingRequest, r) {
// 带loadername!前缀的依赖路径
const s = JSON.stringify(`-!${remainingRequest}`);
// 使用注释webpackChunkName来定义chunkname的语法
return `export default function(cb) {
return cb(import(/* webpackChunkName: "my-lazy-${getChunkNameFromRemainingRequest(
this.resource
)}" */${s}));
}`;
};
использование и официальныеbundle-loaderВ основном то же самое, просто динамический импорт возвращает промис, вам нужно изменить метод использования:
import LazyReactDom from "../loaders/my-bundle!react-dom";
setTimeout(() => {
LazyReactDom(r => {
r.then(({ default: ReactDom }) => {
ReactDom.render(<S />, document.getElementById("root"));
});
});
}, 1000);
контекст загрузчика
Выше мы видели, что это используется при написании загрузчика, и это контекст загрузчика. конкретный видимыйОфициальный сайт
Из множества свойств контекста давайте возьмем одно из них для практики:this.loadModule
loadModule(request: string, callback: function(err, source, sourceMap, module))
loadModuleФункция метода состоит в том, чтобы проанализировать данный запрос к модулю, применить все настроенные загрузчики и передать сгенерированный источник, исходную карту и внутренний веб-пакет в функцию обратного вызова.NormalModuleпример. Вы можете использовать эту функцию, если вам нужно получить исходный код других модулей для получения результатов.
Очевидно, что одним из сценариев применения этого метода является внедрение других зависимостей в существующий код.
let's coding
Справочная информация: уже существует файл API api.js
const api0 = {
log(...args) {
console.log("api log>>>", ...args);
}
};
module.exports = api0;
Эффект надежды: мы используем следующееa.jsjs, вы можете использовать API напрямую, не сообщая об ошибке
// a.js
export default function a() {
return 1;
}
// 其他代码
// ...
api.log("a", "b");
Поэтому, когда нам нужно собрать, загрузчик вставляет апи в наш код:
// addapi的loader
module.exports = function(content, map, meta) {
// 涉及到加载模块,异步loader
const callback = this.async();
this.loadModule("../src/api.js", (err, source, sourceMap, module) => {
// source是一个module.exports = require(xxx)的字符串,我们需要require那部分
callback(
null,
`const api = ${source.split("=")[1]};
${content};`,
sourceMap,
meta
);
});
return;
};
После того, как загрузчик будет написан, не забудьте добавить его в конфигурацию веб-пакета или используйте синтаксис loadername! для импорта a.js (./loaders/addapi!./a.js)
Наконец, мы можем увидеть лог успешного запуска api.js.
Есть также некоторые знакомые сценарии, такие как XX API, XX SDK, методы общедоступных утилит, отчеты pvuv для каждой страницы индекса и т. д. Эти js должны быть загружены, выполнены или импортированы в первую очередь. Если нам лень добавлять файлы по одномуimport/requireЗаявления могут быть завершены мгновенно таким образом.Предпосылка такого рода кокетливой операции состоит в том, чтобы гарантировать, что последующие коллеги возьмутся за проект с низкой сложностью и без ям в коде. Комментарии, документация и элегантное именование — все готово.
наконец
Роль загрузчика заключается в преобразовании всех файлов в js-модули, которые вам нужны и могут использоваться для запуска. Комбинация babel и загрузчика более мощная, вы можете модифицировать код, быть ленивым и так далее. В будущем будут статьи про плагины webpack и babel, давайте учиться и общаться вместе~
Обратите внимание на официальный аккаунт «Другой интерфейс», изучите интерфейс с другой точки зрения, быстро растем, играйте в новейшие технологии и исследуйте различные черные технологии вместе.