За последние два дня, чтобы оптимизировать проект упаковки кода компании, я восполнил много знаний о webpack4. Если бы я изучал webpack несколько лет назад, я бы точно отказался, я видел и раньше старую документацию webpack, которая еще более рудиментарна, чем документация нашего внутреннего проекта.
Но недавно я просмотрел документацию webpack4 и обнаружил, что официальный сайт webpackруководствоНаписано неплохо.Изучить базовую настройку webpack4, следуя этому руководству, не составляет большой проблемы.Друзья, которые хотят систематически изучать webpack, могут глянуть.
Сегодня я в основном делюсь некоторыми из веб-пакетовЛегко перепутать точки знаний, и это также обычное содержание интервью.. Я обобщил содержимое, разбросанное по документации и туториалам, пока что это выглядит так:Единственный во всей сети, каждый можетдобавить в избранное, что удобно для последующего поиска и изучения.
Дружеское напоминание: эта статья не является вводным руководством. Она не будет тратить много чернил на описание базовой конфигурации веб-пакета. Пожалуйста, прочтите исходный код руководства.
Адрес исходного кода учебника на github
1. В вебпакеmodule
,chunk
иbundle
В чем разница?
Честно говоря, когда я впервые начал читать документацию по веб-пакету, я запутался в этих трех терминах и почувствовал, что все они говорят об упаковочных файлах, но через некоторое время в кусках и связках я постепенно потерялся в деталях, так что мы должны выскочить. , посмотрите на эти термины с точки зрения макро.
Официальный сайт webpack сделал чанки и бандлыобъяснять, Честно говоря, это слишком абстрактно.Приведу пример.визуализироватьобъяснять.
Во-первых, мы пишем наш бизнес-код в каталоге src и вводим четыре файла: index.js, utils.js, common.js и index.css, Структура каталога выглядит следующим образом:
src/
├── index.css
├── index.html # 这个是 HTML 模板代码
├── index.js
├── common.js
└── utils.js
index.css напишите немного простой стиль:
body {
background-color: red;
}
Файл utils.js записывает служебную функцию для возведения в квадрат:
export function square(x) {
return x * x;
}
Файл common.js записывает служебную функцию журнала:
module.exports = {
log: (msg) => {
console.log('hello ', msg)
}
}
Внесите несколько простых изменений в файл index.js и добавьте файл css и common.js:
import './index.css';
const { log } = require('./common');
log('webpack');
Конфигурация веб-пакета выглядит следующим образом:
{
entry: {
index: "../src/index.js",
utils: '../src/utils.js',
},
output: {
filename: "[name].bundle.js", // 输出 index.js 和 utils.js
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader, // 创建一个 link 标签
'css-loader', // css-loader 负责解析 CSS 代码, 处理 CSS 中的依赖
],
},
]
}
plugins: [
// 用 MiniCssExtractPlugin 抽离出 css 文件,以 link 标签的形式引入样式文件
new MiniCssExtractPlugin({
filename: 'index.bundle.css' // 输出的 css 文件名为 index.css
}),
]
}
Запустим webpack и посмотрим результат упаковки:
Мы можем видеть, что index.css и common.js представлены в index.js, а упакованные и сгенерированные index.bundle.css и index.bundle.js принадлежат фрагменту 0. Поскольку utils.js упакован независимо, он генерирует utils.bundle.js принадлежит чанку 1.
Чувствуете себя немного неряшливо? Я сделал картинку, вы должны понять это с первого взгляда:
Из этой картинки понятно:
- Для кода с такой же логикой, когда мы пишем следующий файл, будь то ESM, commonJS или AMD, все ониmodule;
- Когда исходный файл модуля, который мы написали, передается веб-пакету для упаковки, веб-пакет генерирует его в соответствии с отношением ссылки на файл.chunkфайл, webpack выполнит некоторые операции с этим файлом фрагмента;
- После того, как webpack обработает файл фрагмента, он, наконец, выведетbundleфайл, этот пакет содержит окончательные исходные файлы, которые были загружены и скомпилированы, поэтому его можно запускать непосредственно в браузере.
Вообще говоря, чанк соответствует пакету, такому как на рисунке выше.utils.js -> chunks 1 -> utils.bundle.js
; Но есть исключения, например, на картинке выше я используюMiniCssExtractPlugin
Извлечено из чанков 0index.bundle.css
документ.
Краткое содержание одной фразы:
module
,chunk
иbundle
На самом деле один и тот же логический код имеет три названия в разных сценариях конвертации:
То, что мы пишем напрямую, является модулем, и веб-пакет обрабатывает его как фрагмент и, наконец, генерирует пакет, который браузер может запускать напрямую.
2.filename
иchunkFilename
разница
filename
имя файла является очень распространенной конфигурацией, которая соответствуетentry
Входной файл внутри, имя выходного файла после упаковки webpack. Например, после следующей конфигурации сгенерированный файл называетсяindex.min.js
.
{
entry: {
index: "../src/index.js"
},
output: {
filename: "[name].min.js", // index.min.js
}
}
chunkFilename
chunkFilename
значит нет в спискеentry
, но нужно упаковатьchunk
Имя файла. Вообще говоря, этоchunk
файловые средстваленивая загрузкакод.
Например, мы написали ленивую загрузку в нашем бизнес-коде.lodash
код:
// 文件:index.js
// 创建一个 button
let btn = document.createElement("button");
btn.innerHTML = "click me";
document.body.appendChild(btn);
// 异步加载代码
async function getAsyncComponent() {
var element = document.createElement('div');
const { default: _ } = await import('lodash');
element.innerHTML = _.join(['Hello!', 'dynamic', 'imports', 'async'], ' ');
return element;
}
// 点击 button 时,懒加载 lodash,在网页上显示 Hello! dynamic imports async
btn.addEventListener('click', () => {
getAsyncComponent().then(component => {
document.body.appendChild(component);
})
})
нашwebpack
Не выполняйте никакую настройку или исходный код конфигурации:
{
entry: {
index: "../src/index.js"
},
output: {
filename: "[name].min.js", // index.min.js
}
}
Результат упаковки на данный момент следующий:
это1.min.js
загружается асинхронноchunk
документ.ДокументацияВот объяснение:
output.chunkFilename
Использовать по умолчанию[id].js
или изoutput.filename
Предполагаемое значение в ([name]
будет предварительно заменен на[id]
или[id].
)
Документация слишком абстрактна, мы могли бы также взглянуть на приведенный выше пример:
output.filename
Имя выходного файла[name].min.js
,[name]
в соответствии сentry
Предполагается, что конфигурацияindex
, поэтому выводindex.min.js
;
так какoutput.chunkFilename
Если нет явной спецификации, он поместит[name]
заменитьchunk
документid
Нет, этот файлid
число равно 1, поэтому имя файла1.min.js
.
Если мы явно настроимchunkFilename
, файл будет сгенерирован по названию конфигурации:
{
entry: {
index: "../src/index.js"
},
output: {
filename: "[name].min.js", // index.min.js
chunkFilename: 'bundle.js', // bundle.js
}
}
Краткое содержание одной фразы:
filename
Ссылаться наперечислены в entry
, имя выходного файла после упаковки.
chunkFilename
Ссылаться нанет в списке entry
, а имя файла, который необходимо запаковать.
3.webpackPrefetch
,webpackPreload
иwebpackChunkName
Для чего это?
Эти термины на самом деле являются веб-пакетомволшебные комментарииВ документе упоминается 6 конфигураций, все конфигурации могут бытькомбинацияВставай и используй его. Поговорим о трех наиболее часто используемых конфигурациях.
webpackChunkName
Асинхронная загрузка упоминалась ранееlodash
например, мы наконец положилиoutput.chunkFilename
писать до смертиbundle.js
. В нашем бизнес-коде невозможно загрузить только один файл асинхронно, поэтому его точно нельзя записать насмерть, а написать как[name].bundle.js
, упакованные файлы представляют собой куски с неясным смыслом и низким уровнем распознаванияid
.
{
entry: {
index: "../src/index.js"
},
output: {
filename: "[name].min.js", // index.min.js
chunkFilename: '[name].bundle.js', // 1.bundle.js,chunk id 为 1,辨识度不高
}
}
В этот моментwebpackChunkName
Вот где это пригодится. мы можемimport
файл, вimport
Псевдоним файла фрагмента в виде комментария:
async function getAsyncComponent() {
var element = document.createElement('div');
// 在 import 的括号里 加注释 /* webpackChunkName: "lodash" */ ,为引入的文件取别名
const { default: _ } = await import(/* webpackChunkName: "lodash" */ 'lodash');
element.innerHTML = _.join(['Hello!', 'dynamic', 'imports', 'async'], ' ');
return element;
}
На данный момент сгенерированный пакетом файл выглядит следующим образом:
Теперь возникает вопрос,lodash
это имя, которое мы взяли, и оно должно быть сгенерировано логическиlodash.bundle.js
а, передvendors~
Что это?
На самом деле ленивая загрузка веб-пакета использует встроенный плагин.SplitChunksPluginРеализовано, есть некоторые в этом плагинеЭлементы конфигурации по умолчанию, сказатьautomaticNameDelimiter
, разделитель по умолчанию~
, поэтому этот символ появится в конечном имени файла.Я не буду расширять это содержание.Заинтересованные студенты могут изучить его самостоятельно.
webpackPrefetch и webpackPreload
Одна из этих двух конфигураций называется Prefetch, а другая – Preload. Они немного отличаются друг от друга. Давайте сначала поговорим об этом.webpackPrefetch
.
В приведенном выше коде отложенной загрузки асинхронная загрузка запускается, когда мы нажимаем кнопкуlodash
действие, на этот раз будет динамически генерироватьсяscript
этикетка, загруженная вhead
заблаговременно:
если мыimport
при добавленииwebpackPrefetch
:
...
const { default: _ } = await import(/* webpackChunkName: "lodash" */ /* webpackPrefetch: true */ 'lodash');
...
будет<link rel="prefetch" as="script">
Предварительно загрузите код lodash в виде:
Этот асинхронно загружаемый код не нужно запускать вручную, нажав кнопку, веб-пакет загрузит его во время простоя после загрузки родительского фрагмента.lodash
документ.
webpackPreload
заключается в предварительной загрузке ресурсов, которые могут потребоваться в рамках текущей навигации, он иwebpackPrefetch
Основные отличия:
- Блок предварительной загрузки начинает загружаться параллельно, когда загружается родительский блок. Блок предварительной выборки начнет загружаться после завершения загрузки родительского блока.
- Блок предварительной загрузки имеет средний приоритет и загружается немедленно. Фрагмент предварительной выборки загружается, когда браузер бездействует.
- Чанк предварительной загрузки будет запрошен сразу в родительском чанке на текущий момент. Блок предварительной выборки будет использоваться в какой-то момент в будущем.
Краткое содержание одной фразы:
webpackChunkName
является псевдонимом предварительно загруженного файла,webpackPrefetch
Файл будет загружен, когда браузер бездействует,webpackPreload
Файлы загружаются параллельно, когда загружается родительский фрагмент.
4.hash
,chunkhash
,contenthash
какая разница?
Во-первых, введение: хеширование обычно используется в сочетании с кэшированием CDN. Если содержимое файла изменится, значение хеш-функции соответствующего файла также изменится, и URL-адрес соответствующей ссылки HTML также изменится, что приведет к тому, что сервер CDN извлечет соответствующие данные с исходного сервера, а затем обновит локальный кеш.
hash
Вычисление хеша связано с построением всего проекта, сделаем простое демо.
Следуя демонстрационному коду случая 1, каталог файлов выглядит следующим образом:
src/
├── index.css
├── index.html
├── index.js
└── utils.js
Основная конфигурация webpack выглядит следующим образом (некоторая информация о конфигурации модуля опущена):
{
entry: {
index: "../src/index.js",
utils: '../src/utils.js',
},
output: {
filename: "[name].[hash].js", // 改为 hash
},
......
plugins: [
new MiniCssExtractPlugin({
filename: 'index.[hash].css' // 改为 hash
}),
]
}
Имена сгенерированных файлов следующие:
Мы можем обнаружить, что хэш сгенерированного файла и хеш сборки проекта абсолютно одинаковы.
chunkhash
Так как хэш это хеш-значение построения проекта, если будут какие-то изменения в проекте, то хеш обязательно изменится.Например я изменил код utils.js.Хотя код в index.js не изменился, все используют одну и ту же копию хэша. При изменении хэша кеш должен быть недействительным, поэтому нет возможности реализовать кеширование CDN и браузера.
chunkhash решает эту проблему: он анализирует зависимые файлы в соответствии с разными входными файлами (Entry), строит соответствующие чанки и генерирует соответствующие хеш-значения.
Возьмем другой пример, вносим изменения в файл utils.js:
export function square(x) {
return x * x;
}
// 增加 cube() 求立方函数
export function cube(x) {
return x * x * x;
}
Затем измените все хэши в webpack на chunkhash:
{
entry: {
index: "../src/index.js",
utils: '../src/utils.js',
},
output: {
filename: "[name].[chunkhash].js", // 改为 chunkhash
},
......
plugins: [
new MiniCssExtractPlugin({
filename: 'index.[chunkhash].css' // // 改为 chunkhash
}),
]
}
Результат сборки следующий:
Мы видим, что хэш чанка 0 такой же, а хэш чанка 1 отличается от приведенного выше.
Предположим, я удаляю функцию cube() из utils.js и снова упаковываю ее:
Для сравнения можно обнаружить, что изменился только хэш чанка 1, а хеш чанка 0 остался прежним.
contenthash
Идем дальше, index.js и index.css это один и тот же чанк, если содержимое index.js изменится, а index.css не изменится, то после упаковки изменится их хэш, который является своеобразным css файлом. напрасно тратить. Как решить эту проблему?
contenthash создаст уникальный хэш на основе содержимого ресурса, то есть, если содержимое файла останется прежним, хэш останется прежним.
Давайте изменим конфигурацию веб-пакета:
{
entry: {
index: "../src/index.js",
utils: '../src/utils.js',
},
output: {
filename: "[name].[chunkhash].js",
},
......
plugins: [
new MiniCssExtractPlugin({
filename: 'index.[contenthash].css' // 这里改为 contenthash
}),
]
}
Мы внесли три модификации в файл index.js (то есть было изменено содержимое вывода функции лога, и она слишком проста, чтобы не написать ее первой), а затем собрали их отдельно, скриншоты результатов такие: :
Мы можем обнаружить, что хэш файла css не изменился.
Краткое содержание одной фразы:
Вычисление хеша связано с построением всего проекта;
вычисление чанка связано с одним и тем же содержимым чанка;
Вычисление хэша содержимого зависит от самого содержимого файла.
5.sourse-map
серединаeval
,cheap
,inline
иmodule
Что каждый означает?
source-map, в нем есть карта, в этом должен быть смысл маппинга. исходная карта является копиейСопоставление файлов для исходного и преобразованного кода. Конкретных принципов много, и заинтересованные студенты могут поискать сами, здесь я не буду много говорить.
Давайте сначала заглянем на официальный сайт, чтобы увидеть, сколько типов исходной карты доступно:
эмммм, 13 видов, прощай.
Если вы присмотритесь, то увидите, что большинство из этих 13eval
,cheap
,inline
иmodule
Для расположения и сочетания этих 4-х слов я составил простенькую таблицу, которая намного проще, чем на официальном сайте:
параметр | Описание параметра |
---|---|
eval | Используются упакованные модулиeval() Выполнить, сопоставление линий может быть неточным; отдельный файл сопоставления не создается |
cheap | Карта карты показывает только строки, а не столбцы, игнорируя исходные карты из загрузчика. |
inline | Файл карты кодируется в формате base64, добавляется в конец файла пакета и не генерирует отдельный файл карты. |
module | Добавлено сопоставление исходной карты загрузчика и сторонних модулей. |
Все еще не понимаете? Вы можете взглянуть на демо.
Мы делаем некоторую настройку для веб-пакета, а devtool специально настраивается для исходной карты.
......
{
devtool: 'source-map',
}
......
В файле index.js для простоты пишем только одну строчку кода, а чтобы получить сообщение об ошибке, мы намеренно написали ее неправильно:
console.lg('hello source-map !') // log 写成 lg
Давайте попробуем некоторые общие конфигурации:
source-map
source-map — самый большой и полный, и он будет генерировать отдельные файлы карты:
Обратите внимание на положение курсора на рисунке ниже, source-map отобразит сообщение об ошибкерядыИнформация:
cheap-sourse-map
дешевый означает дешевый, он не генерирует сопоставление столбцов, и соответствующий объем будет намного меньше Мы сравниваем результаты упаковки source-map, только 1/4 оригинала.
eval-source-map
eval-source-map упакует и запустит модуль с помощью функции eval(), не создавая отдельный файл карты, он отобразит сообщение об ошибкерядыИнформация:
// index.bundle.js 文件
!function(e) {
// ......
// 省略不重要的代码
// ......
}([function(module, exports) {
eval("console.lg('hello source-map !');//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi4vc3JjL2luZGV4Mi5qcz9mNmJjIl0sIm5hbWVzIjpbImNvbnNvbGUiLCJsZyJdLCJtYXBwaW5ncyI6IkFBQUFBLE9BQU8sQ0FBQ0MsRUFBUixDQUFXLG9CQUFYIiwiZmlsZSI6IjAuanMiLCJzb3VyY2VzQ29udGVudCI6WyJjb25zb2xlLmxnKCdoZWxsbyBzb3VyY2UtbWFwICEnKSJdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///0\n")
}
]);
inline-source-map
Файл карты кодируется в формате base64 и добавляется в конец файла пакета, а не создает отдельный файл карты. После добавления файла карты мы можем ясно видеть, что размер пакета стал больше;
// index.bundle.js 文件
!function(e) {
}([function(e, t) {
console.lg("hello source-map !")
}
]);
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4uL3NyYy9pbmRleDIuanMiXSwibmFtZXMiOlsiaW5zdGFsbGVkTW9kdWxlcyIsIl9fd2VicGFja19yZXF1aXJ......
// base64 太长了,我删了一部分,领会精神
Общая конфигурация:
Приведенные выше примеры являются демонстрационными в сочетании сРекомендация официального сайтаИ практический опыт, обычно используемые конфигурации на самом деле таковы:
1.source-map
Он большой и всеобъемлющий, и в нем есть все, потому что все может увеличить время сборки веб-пакета, это зависит от ситуации.
2.cheap-module-eval-source-map
Это обычно рекомендуется для среды разработки (dev) и обеспечивает хороший баланс в напоминании об ошибке скорости построения.
3.cheap-module-source-map
Вообще говоря, производственная среда не оснащена исходной картой, если мы хотим зафиксировать ошибку кода в строке, мы можем использовать это
напиши в конце
На этом статья почти закончена, и позже я напишу несколько статей по оптимизации пакетов webapck.
Обновлено 2019-09-26:
Расширенные статьи по оптимизации пакетов webpack:
От изучения веб-пакета до этого результата прошло почти 2 недели. Лично я считаю, что веб-пакет является частью цепочки инструментов в окончательном анализе. Многие элементы конфигурации не нужно запоминать, как встроенные методы JavaScript. большой и заполните его самостоятельно.Демонстрация, если вы знаете, что может сделать элемент конфигурации, вы можете проверить его, когда захотите его использовать.
Итак, я подытожил эту статью о запутанных знаниях вебпака.Вы можете щелкнуть, чтобы сохранить ее.Когда вы будете готовиться к интервью или обзору в будущем, вы сможете понять это, взглянув на нее.
Напоследок хочу порекомендовать свой личный паблик «Лаборатория яиц в панировке». Обычно я делюсь некоторыми фронтенд-технологиями и контентом для анализа данных. Если вам интересно, то можете обратить внимание на: