👨💻 Интервьюер:Участвовали ли вы в настройке Webpack в проекте вашей компании? Или вы внесли какие-либо оптимизации в конфигурацию?
👨🌾 Я:нет 😅
👨💻 Интервьюер:em...
В это время прозвучала фоновая музыка: "🎵 Я больше всего боюсь внезапной тишины в воздухе~"
На самом деле этоОчень искренний ответ!У многих действительно нет возможности участвовать в настройке Webpack в проекте, не говоря уже об оптимизации конфигурации.
Таким образом, легко получить следующие проблемы:
- Без дополнительных возможностей для практики люди склонны игнорировать ее изучение.
- Когда я хочу учиться, я не знаю с чего начать🤦♂️
В это время мы можем сначала взглянуть на то, что обычно спрашивают о Webpack во время интервью.
Зачем начинать с вопросов для интервью?
- Узнайте, какие знания, связанные с Webpack, конкретно ожидают от кандидатов
- Нам удобно фиксировать очки знаний изучения Webpack
Здесь я нашел несколько репрезентативных вопросов, попробуйте посмотреть, на сколько вы сможете ответить 😉
В: Какие загрузчики используются в конфигурации Webpack? Что оно делает?
В: Какие плагины используются в конфигурации Webpack? Что оно делает?
В: В чем разница между загрузчиком и плагином?
Q: Как написать Loader?Внести идею?
В: Как написать плагин?Внести идею?
Q: Была ли настроена оптимизация Webpack? Можете ли вы сказать это просто?
Q: Как оптимизировать производительность на уровне Webpack?
В: Что такое процесс упаковки Webpack?
В: Каков принцип реализации tree-shaking?
Вопрос. Как работает горячее обновление Webpack (HMR)?
В: Как плагин Babel работает в связке Webpack?
В: В чем сходство и различие между Webpack и Rollup?
В: Какие новые функции были обновлены в Webpack5?
как насчет этого? Эти вопросы в порядке?
Далее мы разбираем и обобщаем эти вопросы интервью и рисуем общую систему знаний
После того, как система знаний установлена, мы сосредоточимся на системе знаний и просто разделим ее на три уровня:
- Основание-- будем настраивать
- Передовой-- можно оптимизировать
- глубоко- понять принцип
Повышайте уровень, чтобы сражаться с монстрами 🦖, отвечайте на вопросы интервью Webpack разной глубины 🤓
Приходите, начнем прямо сейчас 🤜
1. Основы веб-пакета
В первой части, начиная с простого требования «квалифицированной настройки», сначала разберитесь с простой настройкой Webpack и вопросами собеседования, связанными с простой настройкой.
1. Простая конфигурация
Эта часть требует освоения:
- Каковы общие элементы конфигурации Webpack?
- Какие загрузчики обычно используются? Как настроить?
- Каковы общие плагины (Plugin)? Как настроить?
- Как настроить Babel? Как работает плагин Babel?
1.1 Установить зависимости
Без сомнения, сначала установите его локальноwebpack
так же какwebpack-cli
$ npm install webpack webpack-cli -D # 安装到本地依赖
Установка завершена ✅
+ webpack-cli@4.7.2
+ webpack@5.44.0
1.2 Рабочий режим
webpack поддерживает 0 пакетов конфигурации после 4, мы можем это протестировать
- новый
./src/index.js
файл, напишите простой код
const a = 'Hello ITEM'
console.log(a)
module.exports = a;
Структура каталогов на данный момент
webpack_work
├─ src
│ └─ index.js
└─ package.json
- бежать напрямую
npx webpack
, начать упаковку
После завершения упаковки видим в логе сообщение:The 'mode' option has not been set,...
Это значит, что мы не настроили режим, здесь нам напоминают его настроить
модель:Для параметра конфигурации режима скажите веб-пакету использовать встроенную оптимизацию соответствующего режима, значение по умолчанию равно
production
,а такжеdevelopment
,none
, их отличия заключаются в следующем
Опции | описывать |
---|---|
development | Режим разработки, более быстрая упаковка, сохранение шагов оптимизации кода |
production | В производственном режиме упаковка работает медленно, включена древовидная тряска и сжатый код. |
none | Не используйте параметры оптимизации по умолчанию |
Как настроить? очень простой
- Просто укажите параметр режима в объекте конфигурации:
module.exports = {
mode: 'development',
};
- Передано из аргументов CLI:
$ webpack --mode=development
1.3 Файлы конфигурации
Хотя пакетов конфигурации 0, на практике нам все равно нужно использовать файлы конфигурации для удовлетворения потребностей разных проектов.
-
Создайте новый файл конфигурации в корневом пути
webpack.config.js
-
Добавьте базовую информацию о конфигурации
const path = require('path')
module.exports = {
mode: 'development', // 模式
entry: './src/index.js', // 打包入口地址
output: {
filename: 'bundle.js', // 输出文件名
path: path.join(__dirname, 'dist') // 输出文件目录
}
}
Это не много, чтобы сказать, самая базовая конфигурация
1.4 Loader
Здесь мы меняем запись на файл CSS, как может выглядеть результат упаковки?
1. Новый./src/main.css
body {
margin: 0 auto;
padding: 0 20px;
max-width: 800px;
background: #f4f8fb;
}
- Изменить конфигурацию записи
const path = require('path')
module.exports = {
mode: 'development', // 模式
entry: './src/main.css', // 打包入口地址
output: {
filename: 'bundle.css', // 输出文件名
path: path.join(__dirname, 'dist') // 输出文件目录
}
}
3. Запустите команду упаковки:npx webpack
Вот ошибка!
Это потому что:По умолчанию webpack поддерживает обработку файлов JS и JSON, а другие типы не могут быть обработаны, здесь необходимо использовать Loader для обработки разных типов файлов.
- Установить
css-loader
для обработки CSS
npn install css-loader -D
- Настройка модулей загрузки ресурсов
const path = require('path')
module.exports = {
mode: 'development', // 模式
entry: './src/main.css', // 打包入口地址
output: {
filename: 'bundle.css', // 输出文件名
path: path.join(__dirname, 'dist') // 输出文件目录
},
module: {
rules: [ // 转换规则
{
test: /\.css$/, //匹配所有的 css 文件
use: 'css-loader' // use: 对应的 Loader 名称
}
]
}
}
- Повторно запустите команду пакета
npx webpack
Эй, он готов к упаковке 😁
dist
└─ bundle.css # 打包得到的结果
Это попытка здесь, файл входа еще нужно изменить обратно./src/index.js
Здесь мы можем сделать вывод:Загрузчик должен преобразовать контент, который Webpack не знает, в контент, который он знает.
1.5 Плагины
В отличие от Loader, который используется для преобразования определенных типов файлов,Плагины могут проходить через жизненный цикл упаковки Webpack и выполнять различные задачи.
Вот посмотрите на столбец в использовании:
1. Новый./src/index.html
документ
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ITEM</title>
</head>
<body>
</body>
</html>
Если я хочу, чтобы упакованные файлы ресурсов, такие как файлы js или css, можно было автоматически импортировать в HTML, мне нужно использовать плагинhtml-webpack-pluginчтобы помочь вам сделать это
2. Локальная установкаhtml-webpack-plugin
npm install html-webpack-plugin -D
3. Настройте плагин
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development', // 模式
entry: './src/index.js', // 打包入口地址
output: {
filename: 'bundle.js', // 输出文件名
path: path.join(__dirname, 'dist') // 输出文件目录
},
module: {
rules: [
{
test: /\.css$/, //匹配所有的 css 文件
use: 'css-loader' // use: 对应的 Loader 名称
}
]
},
plugins:[ // 配置插件
new HtmlWebpackPlugin({
template: './src/index.html'
})
]
}
Запустите пакет и откройте файл index.html, созданный в каталоге dist.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ITEM</title>
<script defer src="bundle.js"></script></head>
<body>
</body>
</html>
Вы можете увидеть, что он автоматически вводит упаковку Bundle.js, который очень удобно и практичен
1.6 Автоматический каталог пустых пакетов
Каждый раз, когда вы упаковываете, каталог упаковки будет оставлять последние упакованные файлы.Чтобы сохранить каталог упаковки чистым, нам нужно очистить каталог упаковки перед упаковкой.
Здесь мы можем использовать плагиныclean-webpack-pluginреализовать
- Установить
$ npm install clean-webpack-plugin -D
- настроить
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 引入插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
// ...
plugins:[ // 配置插件
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new CleanWebpackPlugin() // 引入插件
]
}
1.7 Различайте окружение
Должны быть разные потребности для локальной разработки и онлайн-развертывания.
Местная среда:
- Нужны более быстрые сборки
- Необходимо распечатать отладочную информацию
- Требуется функция оперативной перезагрузки или горячей перезагрузки
- Исходная карта необходима для облегчения поиска проблемы
- ...
Производственная среда:
- Нужен меньший размер пакета, сжатие кода + встряхивание дерева
- требуется разделение кода
- Нужно сжать размер изображения
- ...
Для разных потребностей первое, что нужно сделать, это отличить окружающую среду
- Установите cross-env локально [адрес документа]
npm install cross-env -D
- Настроить команды запуска
Открыть./package.json
"scripts": {
"dev": "cross-env NODE_ENV=dev webpack serve --mode development",
"test": "cross-env NODE_ENV=test webpack --mode production",
"build": "cross-env NODE_ENV=prod webpack --mode production"
},
- Получить переменные среды в файле конфигурации webpack
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
console.log('process.env.NODE_ENV=', process.env.NODE_ENV) // 打印环境变量
const config = {
entry: './src/index.js', // 打包入口地址
output: {
filename: 'bundle.js', // 输出文件名
path: path.join(__dirname, 'dist') // 输出文件目录
},
module: {
rules: [
{
test: /\.css$/, //匹配所有的 css 文件
use: 'css-loader' // use: 对应的 Loader 名称
}
]
},
plugins:[ // 配置插件
new HtmlWebpackPlugin({
template: './src/index.html'
})
]
}
module.exports = (env, argv) => {
console.log('argv.mode=',argv.mode) // 打印 mode(模式) 值
// 这里可以通过不同的模式修改 config 配置
return config;
}
4. Проверьте это
- воплощать в жизнь
npm run build
process.env.NODE_ENV= prod
argv.mode= production
- воплощать в жизнь
npm run test
process.env.NODE_ENV= test
argv.mode= production
- воплощать в жизнь
npm run dev
process.env.NODE_ENV= dev
argv.mode= development
Таким образом, мы можем динамически изменять конфигурацию Webpack в разных средах.
1.8 Запуск devServer
1. Установкаwebpack-dev-server
npm intall webpack-dev-server@3.11.2 -D
⚠️ Примечание. В этой статье используется
webpack-dev-server
версия3.11.2
, когда версияversion >= 4.0.0
, вам нужно использоватьdevServer.staticнастроить, не болееdevServer.contentBase
элемент конфигурации.
2. Настройте локальные службы
// webpack.config.js
const config = {
// ...
devServer: {
contentBase: path.resolve(__dirname, 'public'), // 静态文件目录
compress: true, //是否启动压缩 gzip
port: 8080, // 端口号
// open:true // 是否自动打开浏览器
},
// ...
}
module.exports = (env, argv) => {
console.log('argv.mode=',argv.mode) // 打印 mode(模式) 值
// 这里可以通过不同的模式修改 config 配置
return config;
}
Зачем настраивать contentBase?
Потому что при упаковке webpack обработка статических файлов, таких как изображения, напрямую копируется в каталог dist. Однако для локальной разработки этот процесс является слишком трудоемким и ненужным, поэтому после настройки contentBase вы можете напрямую читать файл в соответствующем статическом каталоге без какого-либо перемещения файла, что экономит время и снижает нагрузку на производительность.
- запустить локальную службу
$ npm run dev
Чтобы увидеть эффект, я добавил в html кусок текста и поместил изображение logo.png в паблик.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ITEM</title>
</head>
<body>
<p>ITEM</p>
</body>
</html>
public
└─ logo.png
открытый адресhttp://localhost:8080/
Затем доступhttp://localhost:8080/logo.png
Хорошо, без проблем 👌
1.9 Введение в CSS
Выше мы говорили об использовании css-loader для обработки css в Loader, но сам по себе css-loader не может загружать стили на страницы. В настоящее время нам нужно установить другой загрузчик стилей, чтобы завершить эту функцию.
style-loader — добавить обработанный css на страницу в виде тегов стиля
- Установить
style-loader
[адрес документа]
npm install style-loader -D
- Настроить загрузчики
const config = {
// ...
module: {
rules: [
{
test: /\.css$/, //匹配所有的 css 文件
use: ['style-loader','css-loader']
}
]
},
// ...
}
⚠️Примечание:Порядок выполнения Loader фиксируется сзади наперед, то есть нажмите
css-loader --> style-loader
последовательность выполнения
- Файл стиля цитирования
во входном файле./src/index.js
импортировать файл стиля./src/main.css
// ./src/index.js
import './main.css';
const a = 'Hello ITEM'
console.log(a)
module.exports = a;
/* ./src/main.css */
body {
margin: 10px auto;
background: cyan;
max-width: 800px;
}
- Перезапустите локальную службу и посетите
http://localhost:8080/
Этот стиль работает, продолжайте изменять стиль
body {
margin: 10px auto;
background: cyan;
max-width: 800px;
/* 新增 */
font-size: 46px;
font-weight: 600;
color: white;
position: fixed;
left: 50%;
transform: translateX(-50%);
}
После сохранения стиль автоматически изменяется.
Основная логика style-loader эквивалентна:
const content = `${样式内容}`
const style = document.createElement('style');
style.innerHTML = content;
document.head.appendChild(style);
Внедряйте стили на страницы, динамически добавляя теги стилей.
1.10 CSS-совместимость
использоватьpostcss-loader, который автоматически добавляет префиксы браузера к некоторым свойствам CSS3.
выше мы использовалиtransform: translateX(-50%);
, вам нужно добавить различные префиксы браузера, мы можем использовать postcss-loader, чтобы помочь нам выполнить это
npm install postcss-loader postcss -D
const config = {
// ...
module: {
rules: [
{
test: /\.css$/, //匹配所有的 css 文件
use: ['style-loader','css-loader', 'postcss-loader']
}
]
},
// ...
}
⚠️ Здесь есть большая яма:После того, как справочный документ настроен, при запуске будет сообщено об ошибке
Error: Loading PostCSS "postcss-import" plugin failed:
Cannot find module 'postcss-import'
Коллекция плагинов, которые можно попробовать установить позжеpostcss-preset-env
, а затем измените конфигурацию на
// webpack.config.js
// 失败配置
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'postcss-preset-env',
{
// 其他选项
},
],
],
},
},
},
После запуска все равно будет сообщать об ошибке.После ознакомления с информацией я наконец нашел правильный способ открыть его.Давайте сделаем это снова 😁
npm install postcss postcss-loader postcss-preset-env -D
Добавить загрузчик postcss-loader
const config = {
// ...
module: {
rules: [
{
test: /\.css$/, //匹配所有的 css 文件
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
}
]
},
// ...
}
Создайте файл конфигурации postcsspostcss.config.js
// postcss.config.js
module.exports = {
plugins: [require('postcss-preset-env')]
}
Создайте файл конфигурации postcss-preset-env.browserslistrc
# 换行相当于 and
last 2 versions # 回退两个浏览器版本
> 0.5% # 全球超过0.5%人使用的浏览器,可以通过 caniuse.com 查看不同浏览器不同版本占有率
IE 10 # 兼容IE 10
Попробуйте снова
Префикс добавляется автоматически 👏
если ты прав.browserslistrc
Если вас интересуют эффекты различных конфигураций, вы можете использоватьautoprefixerСовершайте онлайн-конверсии, чтобы увидеть результаты
1.11 Представляем Less или Sass
Less и sass также не распознаются Webpack, и вам нужно использовать соответствующий загрузчик, чтобы справиться с ними.
тип файла | loader |
---|---|
Less | less-loader |
Sass | sass-loader node-sass или dart-sass |
Меньшая обработка относительно проста, просто добавьте соответствующий загрузчик напрямую.
Sass нужно больше, чем просто установкаsass-loader
Должен идти с однимnode-sass
,здесьnode-sass
Для установки рекомендуется использовать зеркало Taobao, вероятность успешной установки npm слишком мала 🤣
Здесь мы будем использовать Sass в качестве примера.
- Установить
$ npm install sass-loader -D
# 淘宝镜像
$ npm i node-sass --sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
- новый
./src/sass.scss
Файлы Sass могут иметь суффикс.scss(常用)
или.sass
$color: rgb(190, 23, 168);
body {
p {
background-color: $color;
width: 300px;
height: 300px;
display: block;
text-align: center;
line-height: 300px;
}
}
- Импорт файлов Sass
import './main.css';
import './sass.scss' // 引入 Sass 文件
const a = 'Hello ITEM'
console.log(a)
module.exports = a;
- Изменить настройку
const config = {
// ...
rules: [
{
test: /\.(s[ac]|c)ss$/i, //匹配所有的 sass/scss/css 文件
use: [
'style-loader',
'css-loader',
'postcss-loader',
'sass-loader',
]
},
]
},
// ...
}
Посмотрите на результаты выполнения
успехов 👏
1.12 Отдельные файлы стилей
Впереди мы все зависим отstyle-loader
Добавляйте стили на страницу в виде тегов стилей
Однако чаще мы все надеемся, что его можно внедрить на страницу в виде CSS-файла.
- Установить
mini-css-extract-plugin
$ npm install mini-css-extract-plugin -D
- Исправлять
webpack.config.js
настроить
// ...
// 引入插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const config = {
// ...
module: {
rules: [
// ...
{
test: /\.(s[ac]|c)ss$/i, //匹配所有的 sass/scss/css 文件
use: [
// 'style-loader',
MiniCssExtractPlugin.loader, // 添加 loader
'css-loader',
'postcss-loader',
'sass-loader',
]
},
]
},
// ...
plugins:[ // 配置插件
// ...
new MiniCssExtractPlugin({ // 添加插件
filename: '[name].[hash:8].css'
}),
// ...
]
}
// ...
- Посмотреть результаты упаковки
dist
├─ avatar.d4d42d52.png
├─ bundle.js
├─ index.html
├─ logo.56482c77.png
└─ main.3bcbae64.css # 生成的样式文件
1.13 Файлы изображений и шрифтов
Хотя при настройке среды разработки выше мы можем установитьcontentBase
Перейдите непосредственно к чтению статических файлов класса изображений и посмотрите на использование следующих двух изображений.
- прямой импорт страницы
<!-- 本地可以访问,生产环境会找不到图片 -->
<img src="/logo.png" alt="">
- Введение фонового изображения
<div id="imgBox"></div>
/* ./src/main.css */
...
#imgBox {
height: 400px;
width: 400px;
background: url('../public/logo.png');
background-size: contain;
}
сообщит об ошибке напрямую
Так что на самом деле Webpack не может распознавать файлы изображений и нуждается в обработке при упаковке
Обычно используемые загрузчики для обработки файлов изображений включают:
Loader | иллюстрировать |
---|---|
file-loader | Решите проблему с введением изображения и скопируйте изображение в указанный каталог, по умолчанию dist |
url-loader | Решение зависит от файла-загрузчика.Когда изображение меньше предельного значения, изображение будет преобразовано в кодировку base64.Когда значение больше предельного значения, файл-загрузчик по-прежнему используется для копирования. |
img-loader | Сжать изображение |
- Установить
file-loader
npm install file-loader -D
- Изменить настройку
const config = {
//...
module: {
rules: [
{
// ...
},
{
test: /\.(jpe?g|png|gif)$/i, // 匹配图片文件
use:[
'file-loader' // 使用 file-loader
]
}
]
},
// ...
}
3. Представьте фотографии
<!-- ./src/index.html -->
<!DOCTYPE html>
<html lang="en">
...
<body>
<p></p>
<div id="imgBox"></div>
</body>
</html>
Представлен в файле стиля
/* ./src/sass.scss */
$color: rgb(190, 23, 168);
body {
p {
width: 300px;
height: 300px;
display: block;
text-align: center;
line-height: 300px;
background: url('../public/logo.png');
background-size: contain;
}
}
импортированный файл js
import './main.css';
import './sass.scss'
import logo from '../public/avatar.png'
const a = 'Hello ITEM'
console.log(a)
const img = new Image()
img.src = logo
document.getElementById('imgBox').appendChild(img)
Запускаем сервис, посмотрим на эффект
Отображение нормальное ✌️
Мы видим, что имя файла изображения было изменено, и значение хеш-функции было добавлено, а затем я взгляну на каталог пакета.
dist
├─ 56482c77280b3c4ad2f083b727dfcbf9.png
├─ bundle.js
├─ d4d42d529da4b5120ac85878f6f69694.png
└─ index.html
В каталоге dist есть еще два файла, которые копируются файловым загрузчиком.
Если вы хотите изменить имя, вы можете добавить конфигурацию
const config = {
//...
module: {
rules: [
{
// ...
},
{
test: /\.(jpe?g|png|gif)$/i,
use:[
{
loader: 'file-loader',
options: {
name: '[name][hash:8].[ext]'
}
}
]
},
{
loader: 'file-loader',
options: {
name: '[name][hash:8].[ext]'
}
}
]
},
// ...
}
Упакуйте его снова
dist
├─ avatard4d42d52.png
├─ bundle.js
├─ index.html
└─ logo56482c77.png
Взгляните еще раз на url-loader
- Установить
url-loader
$ npm install url-loader -D
- настроить
url-loader
Конфигурация аналогична файловому загрузчику, но есть еще одна предельная конфигурация
const config = {
//...
module: {
rules: [
{
// ...
},
{
test: /\.(jpe?g|png|gif)$/i,
use:[
{
loader: 'url-loader',
options: {
name: '[name][hash:8].[ext]',
// 文件小于 50k 会转换为 base64,大于则拷贝文件
limit: 50 * 1024
}
}
]
},
]
},
// ...
}
Взгляните на объем наших двух файлов изображений.
public
├─ avatar.png # 167kb
└─ logo.png # 43kb
Упакуем и посмотрим на эффект
Очевидно, вы можете видеть, что файл logo.png был преобразован в base64 👌
Посмотрите на обработку файлов шрифтов
- Настроить файлы шрифтов
Во-первых, изiconfont.cnЗагрузите файл шрифта на локальный
В проекте создайте новый./src/fonts
папка для хранения файлов шрифтов
Затем импортируйте в файл ввода
// ./src/index.js
import './main.css';
import './sass.scss'
import logo from '../public/avatar.png'
// 引入字体图标文件
import './fonts/iconfont.css'
const a = 'Hello ITEM'
console.log(a)
const img = new Image()
img.src = logo
document.getElementById('imgBox').appendChild(img)
Далее, в./src/index.html
используется в
<!DOCTYPE html>
<html lang="en">
...
<body>
<p></p>
<!-- 使用字体图标文件 -->
<!-- 1)iconfont 对应 font-family 设置的值-->
<!-- 2)icon-member 图标 class 名称可以在 iconfont.cn 中查找-->
<i class="iconfont icon-member"></i>
<div id="imgBox"></div>
</body>
</html>
Наконец, добавьте конфигурацию файла шрифта
const config = {
// ...
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 匹配字体文件
use: [
{
loader: 'url-loader',
options: {
name: 'fonts/[name][hash:8].[ext]', // 体积大于 10KB 打包到 fonts 目录下
limit: 10 * 1024,
}
}
]
},
// ...
}
Упакуйте его и посмотрите на эффект
Но в webpack5 встроен модуль обработки ресурсов,file-loader
а такжеurl-loader
нет необходимости устанавливать
1.14 Использование модулей ресурсов
webpack5 добавляет модуль ресурсов, который позволяет использовать файлы ресурсов (шрифты, значки и т. д.) без настройки дополнительных загрузчиков.
Модуль ресурсов поддерживает следующие четыре конфигурации:
asset/resource
Разделите ресурсы на отдельные файлы и экспортируйте URL-адреса, как в предыдущей функции загрузчика файлов.asset/inline
Экспортировать ресурс в виде dataUrl, аналогично функции предыдущего url-загрузчика, когда параметр меньше лимита.asset/source
Экспорт ресурсов в виде исходного кода.asset
Какой тип использовать будет выбран в зависимости от размера файла.Когда размер файла меньше 8 КБ (по умолчанию), будет использоваться актив/встроенный, в противном случае будет использоваться актив/ресурс.
Вставьте измененный полный код
// ./src/index.js
const config = {
// ...
module: {
rules: [
// ...
{
test: /\.(jpe?g|png|gif)$/i,
type: 'asset',
generator: {
// 输出文件位置以及文件名
// [ext] 自带 "." 这个与 url-loader 配置不同
filename: "[name][hash:8][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 50 * 1024 //超过50kb不转 base64
}
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
type: 'asset',
generator: {
// 输出文件位置以及文件名
filename: "[name][hash:8][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 10 * 1024 // 超过100kb不转 base64
}
}
},
]
},
// ...
}
module.exports = (env, argv) => {
console.log('argv.mode=',argv.mode) // 打印 mode(模式) 值
// 这里可以通过不同的模式修改 config 配置
return config;
}
Выполнить упаковку, результат такой же, как и раньше
1.15 JS-совместимость (Вавилон)
В процессе разработки мы хотим использовать последние функции Js, но поддержка браузерами некоторых новых функций не очень хороша, поэтому Js также должна быть совместима, и распространенный способ — преобразовать синтаксис ES6 в ES5.
Здесь появится «самый красивый мальчик» - Бабель
- Бабель не настроен
Давайте напишем что-нибудь ES6
// ./src/index.js
import './main.css';
import './sass.scss'
import logo from '../public/avatar.png'
import './fonts/iconfont.css'
// ...
class Author {
name = 'ITEM'
age = 18
email = 'lxp_work@163.com'
info = () => {
return {
name: this.name,
age: this.age,
email: this.email
}
}
}
module.exports = Author
Для облегчения просмотра исходного кода меняем режим наdevelopment
Затем выполните команду пакета
После упаковки открытьbundle.js
Просмотр упакованных результатов
Хотя мы все еще можем найти наш код, но он не интуитивно понятен, мы сначала устанавливаем режим наnone
, упакованный в самом примитивном виде, а потом посмотрите на результаты упаковки
Запакованный код сильно не меняется, но адрес изображения заменяется.Дальше посмотрим как изменится запакованный результат после настройки babel
- Установить зависимости
$ npm install babel-loader @babel/core @babel/preset-env -D
-
babel-loader
Загрузите код ES2015+ и преобразуйте его в ES5 с помощью Babel. -
@babel/core
Базовый пакет, скомпилированный Babel -
@babel/preset-env
Скомпилированные Babel пресеты, которые можно рассматривать как расширенный набор плагинов Babel.
- Настройка пресетов Babel
// webpack.config.js
// ...
const config = {
entry: './src/index.js', // 打包入口地址
output: {
filename: 'bundle.js', // 输出文件名
path: path.join(__dirname, 'dist'), // 输出文件目录
},
module: {
rules: [
{
test: /\.js$/i,
use: [
{
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
],
}
}
]
},
// ...
]
},
//...
}
// ...
После завершения настройки запустите пакет
Только что написанный класс ES6 был преобразован в форму конструктора ES5.
Что касается обработки совместимости, мы можем, естественно, указать, какие браузеры должны быть совместимы с
чтобы избежатьwebpack.config.js
Слишком раздут, рекомендуется извлечь конфигурационный файл Babel
Добавить в корневой каталог.babelrc.js
// ./babelrc.js
module.exports = {
presets: [
[
"@babel/preset-env",
{
// useBuiltIns: false 默认值,无视浏览器兼容配置,引入所有 polyfill
// useBuiltIns: entry 根据配置的浏览器兼容,引入浏览器不兼容的 polyfill
// useBuiltIns: usage 会根据配置的浏览器兼容,以及你代码中用到的 API 来进行 polyfill,实现了按需添加
useBuiltIns: "entry",
corejs: "3.9.1", // 是 core-js 版本号
targets: {
chrome: "58",
ie: "11",
},
},
],
],
};
Ну вот и настроен простенький Babel пресет
Общие пресеты Babel также:
@babel/preset-flow
@babel/preset-react
@babel/preset-typescript
Если интересно, можете узнать сами.Мы не будем здесь распространяться.Поговорим об использовании плагинов.
- Настройте плагин Babel
Babel не может обрабатывать новые фичи, которые предлагаются, но еще не вошли в спецификацию ECMA, должны быть установлены соответствующие плагины, например:
// ./ index.js
import './main.css';
import './sass.scss'
import logo from '../public/avatar.png'
import './fonts/iconfont.css'
const a = 'Hello ITEM'
console.log(a)
const img = new Image()
img.src = logo
document.getElementById('imgBox').appendChild(img)
// 新增装饰器的使用
@log('hi')
class MyClass { }
function log(text) {
return function(target) {
target.prototype.logger = () => `${text},${target.name}`
}
}
const test = new MyClass()
test.logger()
Выполнить пакет
Неудивительно, что не узнал 🙅🏻♀️
Как я могу его использовать? Babel фактически предоставляет соответствующие плагины:
@babel/plugin-proposal-decorators
@babel/plugin-proposal-class-properties
Установите его:
$ npm install babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties -D
Открыть.babelrc.js
плюс настройка плагина
module.exports = {
presets: [
[
"@babel/preset-env",
{
useBuiltIns: "entry",
corejs: "3.9.1",
targets: {
chrome: "58",
ie: "11",
},
},
],
],
plugins: [
["@babel/plugin-proposal-decorators", { legacy: true }],
["@babel/plugin-proposal-class-properties", { loose: true }],
]
};
Так что его можно упаковать вbundle.js
Код Js, который был преобразован в поддержку браузера
Точно так же мы можем использовать различные плагины в соответствии с нашими реальными потребностями.
2 варианта конфигурации SourceMap
SourceMap — это отношение сопоставления.Когда проект запускается, если возникает ошибка, мы можем использовать SourceMap для обратного определения местоположения исходного кода.
2.1 конфигурация инструмента разработки
const config = {
entry: './src/index.js', // 打包入口地址
output: {
filename: 'bundle.js', // 输出文件名
path: path.join(__dirname, 'dist'), // 输出文件目录
},
devtool: 'source-map',
module: {
// ...
}
// ...
После выполнения пакета в каталоге dist будет сгенерировано следующее..map
Файл SourceMap в конце
dist
├─ avatard4d42d52.png
├─ bundle.js
├─ bundle.js.map
└─ index.html
Кромеsource-map
В дополнение к этому типу, существует множество типов, которые можно использовать, например:
eval
eval-source-map
cheap-source-map
inline-source-map
cheap-module-source-map
inline-cheap-source-map
cheap-module-eval-source-map
inline-cheap-module-source-map
hidden-source-map
nosources-source-map
Так много, какая разница? Как выбрать?
2.2 Различия элементов конфигурации
- Для удобства сравнения их различий мысоздать новый проект
webpack_source_map
├─ src
│ ├─ Author.js
│ └─ index.js
├─ package.json
└─ webpack.config.js
- Открыть
./src/Author.js
class Author {
name = 'ITEM'
age = 18
email = 'lxp_work@163.com'
info = () => {
return {
name: this.name,
age: this.age,
email: this.email
}
}
}
module.exports = Author
- Открыть
./src/index.js
import Author from './Author'
const a = 'Hello ITEM'
console.log(a)
const img = new Image()
img.src = logo
document.getElementById('imgBox').appendChild(img)
const author = new Author();
console.log(author.info)
- Открыть
package.json
{
"name": "webpack-source-map",
"version": "1.0.0",
"description": "",
"main": "index.js",
"license": "MIT",
"scripts": {
"build": "webpack"
},
"devDependencies": {
"@babel/core": "^7.6.4",
"@babel/preset-env": "^7.6.3",
"babel-loader": "^8.0.6",
"html-webpack-plugin": "^3.2.0",
"webpack": "^5.44.0",
"webpack-cli": "^4.7.2"
}
}
- Открыть
webpack.config.js
// 多入口打包
module.exports = [
{
entry: './src/index.js',
output: {
filename: 'a.js'
}
},
{
entry: './src/index.js',
output: {
filename: 'b.js'
}
}
]
Выполните команду пакетаnpm run build
, посмотрите на результаты
dist
├─ a.js
└─ b.js
Не заботьтесь о результатах упаковкиa.js b.js
Что внутри, цель этого шага — протестировать мультивходную упаковку
Цель преобразования в несколько входов - облегчить наше сравнение позже.
- Используйте отдельные записи упаковки для разных элементов конфигурации, откройте
webpack.config.js
Исправлять
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 1)定义不同的打包类型
const allModes = [
'eval',
'source-map',
'eval-source-map',
'cheap-source-map',
'inline-source-map',
'cheap-eval-source-map',
'cheap-module-source-map',
'inline-cheap-source-map',
'cheap-module-eval-source-map',
'inline-cheap-module-source-map',
'hidden-source-map',
'nosources-source-map'
]
// 2)循环不同 SourceMap 模式,生成多个打包入口
module.exports = allModes.map(item => {
return {
devtool: item,
mode: 'none',
entry: './src/main.js',
output: {
filename: `js/${item}.js`
},
module: {
rules: [
{
test: /.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
},
plugins: [
3)输出到不同的页面
new HtmlWebpackPlugin({
filename: `${item}.html`
})
]
}
}
- Ошибка кода моделирования
// ./src/index.js
import Author from './Author'
const a = 'Hello ITEM'
// 故意使用了错误的 console.log
console.log11(a)
const img = new Image()
img.src = logo
document.getElementById('imgBox').appendChild(img)
const author = new Author();
console.log(author.info)
- попробуй упаковать
Сообщил неправильно!!
Выдается подсказка, что название режима SourceMap неправильное, получается, что их склейка регулярная и осмысленная.
Соблюдаем правила проверки^(inline-|hidden-|eval-)?(nosources-)?(cheap-(module-)?)?source-map$
чек об оплате
cheap-eval-source-map
а такжеcheap-module-eval-source-map
Кажется, есть проблема,eval
Бегите назад, изменить его
// 修改之后
const allModes = [
'eval',
'source-map',
'eval-source-map',
'cheap-source-map',
'inline-source-map',
'eval-cheap-source-map',
'cheap-module-source-map',
'inline-cheap-source-map',
'eval-cheap-module-source-map',
'inline-cheap-module-source-map',
'hidden-source-map',
'nosources-source-map'
]
Выполнить пакет снова
Ошибка все равно есть! ! затем изменить
Я проверил сообщение об ошибке, вероятностьhtml-webpack-pugin
Версия слишком старая для совместимости с webpack5, давайте обновим версию до"html-webpack-plugin": "^5.3.2"
попробуй еще раз, все нормально
dist
├─ js
│ ├─ cheap-module-source-map.js #............ 有对应的 .map 文件
│ ├─ cheap-module-source-map.js.map
│ ├─ cheap-source-map.js #................... 有
│ ├─ cheap-source-map.js.map
│ ├─ eval-cheap-module-source-map.js #....... 无
│ ├─ eval-cheap-source-map.js #.............. 无
│ ├─ eval-source-map.js #.................... 无
│ ├─ eval.js #............................... 无
│ ├─ hidden-source-map.js #.................. 有
│ ├─ hidden-source-map.js.map
│ ├─ inline-cheap-module-source-map.js #..... 无
│ ├─ inline-cheap-source-map.js #............ 无
│ ├─ inline-source-map.js #.................. 无
│ ├─ nosources-source-map.js #............... 有
│ ├─ nosources-source-map.js.map
│ ├─ source-map.js #......................... 有
│ └─ source-map.js.map
├─ cheap-module-source-map.html
├─ cheap-source-map.html
├─ eval-cheap-module-source-map.html
├─ eval-cheap-source-map.html
├─ eval-source-map.html
├─ eval.html
├─ hidden-source-map.html
├─ inline-cheap-module-source-map.html
├─ inline-cheap-source-map.html
├─ inline-source-map.html
├─ nosources-source-map.html
└─ source-map.html
Из структуры каталогов мы можем легко увидеть, чтоeval
а такжеinline
Не имеют соответствующего шаблона.map
Файл, в частности, почему следующий анализ
Далее запускаем сервис в каталоге dist и открываем его в браузере
Затем мы анализируем один за другим
eval
модель:
- Сгенерировать код через
eval
Выполнить 👇🏻
@sourceURL
source-map
eval-source-map
eval
dataUrl
eval-cheap-source-map
eval
- dataUrl
eval-cheap-module-source-map
eval
- dataUrl
inline-source-map
- dataUrl
source-map
тот же образец
hidden-source-map
модель:
- Не видно эффекта SourceMap, но создается файл SourceMap
nosources-source-map
модель:
- Вы можете увидеть, где возникает ошибка 👇🏻
- Но нет возможности реально соответствовать исходному коду
Далее немного подытожим:
devtool | build | rebuild | показать код | Файл исходной карты | описывать |
---|---|---|---|---|---|
(none) | скоро | скоро | без | без | Не удалось найти ошибку |
eval | быстро | очень скоро (кэш) | после компиляции | без | Перейти к файлу |
source-map | очень медленно | очень медленно | исходный код | имеют | положение для гребли |
eval-source-map | очень медленно | общий (кэш) | после компиляции | имеет (URL-адрес данных) | положение для гребли |
eval-cheap-source-map | в общем | быстро (кэш) | после компиляции | имеет (URL-адрес данных) | найти ряд |
eval-cheap-module-source-map | медленный | быстро (кэш) | исходный код | имеет (URL-адрес данных) | найти ряд |
inline-source-map | очень медленно | очень медленно | исходный код | имеет (URL-адрес данных) | положение для гребли |
hidden-source-map | очень медленно | очень медленно | исходный код | имеют | Не удалось найти ошибку |
nosource-source-map | очень медленно | очень медленно | исходный код | без | Перейти к файлу |
Ознакомьтесь с правилами проверки^(inline-|hidden-|eval-)?(nosources-)?(cheap-(module-)?)?source-map$
Анализировать ключевые слова
ключевые слова | описывать |
---|---|
inline | Введите SourceMap в виде dataUrl в код |
hidden | Создавайте файлы SourceMap, но не используйте |
eval |
eval(...) Выполните код в форме и импортируйте SourceMap в виде dataUrl |
nosources | Не генерирует SourceMap |
cheap | Нужно только найти информацию о строке, а не информацию о столбце |
module | Покажите, где ошибка в исходном коде |
Ну вот и анализируется SourceMap.
2.3 Рекомендуемая конфигурация
- Местное развитие:
рекомендовать:eval-cheap-module-source-map
причина:
- Не беда, если первый пакет локальной разработки будет медленным, потому что
eval
Причина в кеше, ребилд будет очень быстрым - В разработке мы не будем писать слишком долго каждую строчку кода, нам нужно только найти строчку, так что добавим
cheap
- Мы хотим иметь возможность находить ошибки в исходном коде, а не в запакованном, поэтому нам нужно добавить
modele
- Производственная среда:
рекомендовать:(none)
причина:
- Я просто не хочу, чтобы другие видели мой исходный код
3. Три значения хэша
Стратегия снятия отпечатков файлов Webpack заключается в добавлении хеш-значения к имени файла. Особенно при использовании CDN кеширование является его особенностью и преимуществом, но если в имени запакованного файла нет суффикса хеш, вас точно замучает кеширование 😂
Например, мы использовали в базовой конфигурации:filename: "[name][hash:8][ext]"
здесь внутри[]
Обернутые, называемые плейсхолдерами, что они означают? Пожалуйста, смотрите таблицу ниже 👇🏻
Заполнитель | объяснять |
---|---|
ext | расширение файла |
name | имя файла |
path | относительный путь к файлу |
folder | Папка |
hash | Уникальное хэш-значение, сгенерированное для каждой сборки |
chunkhash | Сгенерировать хеш-значение на основе чанка |
contenthash | Генерация хеш-значения на основе содержимого файла |
в видеhash
,chunkhash
,contenthash
Вы все еще можете не знать разницы
- hash: если какой-либо файл изменится, изменится хеш-значение сборки всего проекта;
- chunkhash: изменения в файле повлияют только на хеш-значение чанка, в котором он находится;
- contenthash: каждый файл имеет отдельное хеш-значение, и изменения в файле повлияют только на его собственное хэш-значение;
2. Расширенный веб-пакет
Во второй части будем двигаться в сторону "оптимизации" 🏃
Помимо оптимизации конфигурации, нам также необходимо научиться самостоятельно разрабатывать загрузчик и плагин.
1. Оптимизируйте скорость сборки
1.1 Анализ трудоемкости строительства
Здесь нам нужно использовать плагинspeed-measure-webpack-plugin, мы обращаемся к документации для настройки
- Сначала установите его
$ npm i -D speed-measure-webpack-plugin
- Измените наш файл конфигурации webpack.config.js
...
// 费时分析
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
...
const config = {...}
module.exports = (env, argv) => {
// 这里可以通过不同的模式修改 config 配置
return smp.wrap(config);
}
- выполнить упаковку
Сообщил об ошибке 🤦🏻♂️
Это обнажает один из недостатков использования этого плагина, а именно:
- Некоторые новые версии загрузчиков или плагинов будут несовместимы, и их необходимо будет обновить.
Здесь у нас естьmini-css-extract-plugin
Выполните процесс понижения версии:^2.1.0
-> ^1.3.6
Переустановите зависимости и снова выполните упаковку
После понижения версии все равно выдает ошибку.Согласно подсказке,добавляем конфигурациюpublicPath: './'
output: {
filename: 'bundle.js', // 输出文件名
path: path.join(__dirname, 'dist'), // 输出文件目录
publicPath: './'
},
попытка один раз
Это сработало!
Примечание. Очень неэкономично понижать версию плагинов или изменять запись конфигурации, чтобы использовать трудоемкий анализ в webpack 5.x., из-за необходимости демонстрации я продолжу использовать его позже, но в обычной разработке рекомендуется не использовать его.
1.2 Оптимизация конфигурации разрешения
1.2.1 alias
Создайте псевдоним дляimport
или require
Псевдоним используется для упрощения ссылки на модуль, который в основном необходимо настроить в проекте.
const path = require('path')
...
// 路径处理方法
function resolve(dir){
return path.join(__dirname, dir);
}
const config = {
...
resolve:{
// 配置别名
alias: {
'~': resolve('src'),
'@': resolve('src'),
'components': resolve('src/components'),
}
}
};
После завершения настройки мы можем в проекте
// 使用 src 别名 ~
import '~/fonts/iconfont.css'
// 使用 src 别名 @
import '@/fonts/iconfont.css'
// 使用 components 别名
import footer from "components/footer";
1.2.2 extensions
конфигурация веб-пакета по умолчанию
const config = {
//...
resolve: {
extensions: ['.js', '.json', '.wasm'],
},
};
Если пользователь импортирует модуль без расширения, например.
import file from '../path/to/file';
Затем webpack попытается разобрать модули по порядку слева направо в массиве, сконфигурированном расширениями.
нужно знать, это:
- Суффикс имени высокочастотного файла помещается впереди;
- После ручной настройки конфигурация по умолчанию будет перезаписана.
Если вы хотите сохранить конфигурацию по умолчанию, вы можете использовать...
Оператор распространения представляет конфигурацию по умолчанию, например.
const config = {
//...
resolve: {
extensions: ['.ts', '...'],
},
};
1.2.3 modules
Сообщите веб-пакету, в каком каталоге он должен искать при разборе модулей.
const path = require('path');
// 路径处理方法
function resolve(dir){
return path.join(__dirname, dir);
}
const config = {
//...
resolve: {
modules: [resolve('src'), 'node_modules'],
},
};
Скажите webpack сначала искать файлы для анализа в каталоге src, что значительно сэкономит время поиска.
1.2.4 resolveLoader
resolveLoader имеет тот же набор свойств, что и объект разрешения выше, но используется только для разрешения пакетов веб-пакетов.loader Мешок.
В общем, вы можете оставить конфигурацию по умолчанию, но если у вас есть собственный загрузчик, вам нужно настроить его, это может вызвать ошибку, потому что загрузчик не может быть найден
- Например: кладем в папку loader свой собственный загрузчик
Мы можем настроить как
const path = require('path');
// 路径处理方法
function resolve(dir){
return path.join(__dirname, dir);
}
const config = {
//...
resolveLoader: {
modules: ['node_modules',resolve('loader')]
},
};
1.3 externals
externals
Параметр конфигурации предоставляет "Исключить зависимости из выходного пакета"Методы. Эта функция обычноразработчик библиотекиявляется наиболее полезным, однако существуют различные приложения, которые его используют.
Например, импортируйте jQuery из CDN вместо упаковки:
- Ввести ссылки
<script
src="https://code.jquery.com/jquery-3.1.0.js"
integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
crossorigin="anonymous"
></script>
- настроить внешние
const config = {
//...
externals: {
jquery: 'jQuery',
},
};
- Используйте jQuery
import $ from 'jquery';
$('.my-element').animate(/* ... */);
Мы можем использовать этот метод для удаления некоторых зависимостей, которые не нужно изменять, что значительно экономит время на упаковку и создание.
1.3 Сузить область применения
При настройке загрузчика нам нужно указать каталог роли загрузчика или каталог, который необходимо исключить более точно.include
а такжеexclude
Два элемента конфигурации могут выполнять эту функцию, общие примеры:
-
include
: анализирует модули, соответствующие условиям -
exclude
: исключить подходящие модули без разбора -
exclude
более высокий приоритет
Например, при настройке babel
const path = require('path');
// 路径处理方法
function resolve(dir){
return path.join(__dirname, dir);
}
const config = {
//...
module: {
noParse: /jquery|lodash/,
rules: [
{
test: /\.js$/i,
include: resolve('src'),
exclude: /node_modules/,
use: [
'babel-loader',
]
},
// ...
]
}
};
1.3 noParse
- Нет необходимости разрешать зависимые крупномасштабные сторонние библиотеки классов и т. д., и их можно настроить с помощью этого поля для повышения скорости построения.
- Файлы модулей, проигнорированные с помощью noParse, не будут проанализированы.
import
,require
Равная грамматика
const config = {
//...
module: {
noParse: /jquery|lodash/,
rules:[...]
}
};
1.4 IgnorePlugin
предотвратить вimport
или require
При вызове создает модули, соответствующие следующим регулярным выражениям:
-
requestRegExp
Регулярное выражение, которое соответствует (тестовым) путям запроса ресурсов. -
contextRegExp
Регулярное выражение, которое соответствует (проверяет) контексту ресурса (каталогу).
new webpack.IgnorePlugin({ resourceRegExp, contextRegExp });
Следующие примеры демонстрируют несколько вариантов использования этого плагина.
- Установить плагин момента (библиотека обработки времени)
$ npm i -S moment
- Настроить плагин игнорирования
// 引入 webpack
const webpack = require('webpack')
const config = {
...
plugins:[ // 配置插件
...
new webpack.IgnorePlugin({
resourceRegExp: /^\.\/locale$/,
contextRegExp: /moment$/,
}),
]
};
Цель состоит в том, чтобы исключить некитайские голоса в плагине, чтобы можно было значительно сэкономить объем упаковки.
1.5 Многопроцессная конфигурация
Уведомление: На самом деле, в небольших проектах включение мультипроцессной упаковки увеличит временные затраты, потому что будут определенные накладные расходы на запуск процесса и межпроцессное взаимодействие.
1.5.1 thread-loader
настроен вthread-loaderПоследующие загрузчики будут запускаться в отдельном рабочем пуле (worker pool).
- Установить
$ npm i -D thread-loader
- настроить
const path = require('path');
// 路径处理方法
function resolve(dir){
return path.join(__dirname, dir);
}
const config = {
//...
module: {
noParse: /jquery|lodash/,
rules: [
{
test: /\.js$/i,
include: resolve('src'),
exclude: /node_modules/,
use: [
{
loader: 'thread-loader', // 开启多进程打包
options: {
worker: 3,
}
},
'babel-loader',
]
},
// ...
]
}
};
1.5.2 хэппипак ❌
Кроме того, инструмент для включения многопроцессорной упаковки, webpack5 устарел.
1.6 Использование кеша
Использование кэширования может значительно ускорить повторяющиеся сборки.
1.6.1 babel-loader включает кэширование
- У Babel большие временные затраты в процессе трансляции js, результат выполнения babel-loader кешируется, а при перепаковке кэш читается напрямую.
- Расположение кэша:
node_modules/.cache/babel-loader
Конкретная конфигурация выглядит следующим образом:
const config = {
module: {
noParse: /jquery|lodash/,
rules: [
{
test: /\.js$/i,
include: resolve('src'),
exclude: /node_modules/,
use: [
// ...
{
loader: 'babel-loader',
options: {
cacheDirectory: true // 启用缓存
}
},
]
},
// ...
]
}
}
Как другие загрузчики кэшируют результаты?
cache-loaderможет помочь нам сделать это
1.6.2 cache-loader
- Кэшировать результаты обработки некоторых загрузчиков с высокой производительностью.
- Расположение кэша:
node_modules/.cache/cache-loader
- Установить
$ npm i -D cache-loader
- Настроить загрузчик кеша
const config = {
module: {
// ...
rules: [
{
test: /\.(s[ac]|c)ss$/i, //匹配所有的 sass/scss/css 文件
use: [
// 'style-loader',
MiniCssExtractPlugin.loader,
'cache-loader', // 获取前面 loader 转换的结果
'css-loader',
'postcss-loader',
'sass-loader',
]
},
// ...
]
}
}
1.6.3 hard-source-webpack-plugin
- hard-source-webpack-pluginОбеспечивает промежуточный кеш для модулей, а время повторной сборки можно сократить примерно на 80%, но вКэш модуля уже встроен в webpack5, больше не нужно использовать этот плагин.
1.6.4 dll ❌
В webpack5.x больше не рекомендуется использовать этот метод для кэширования модулей, поскольку он имеет встроенный метод кэширования для лучшего восприятия.
1.6.5 постоянный кэш-память
по конфигурацииcacheКэшируйте сгенерированные модули и фрагменты webpack для повышения скорости сборки.
const config = {
cache: {
type: 'filesystem',
},
};
2. Оптимизируйте результаты сборки
2.1 Анализ результатов строительства
С помощью плагиновwebpack-bundle-analyzerМы можем интуитивно видеть размер файла, зависимости каждого модуля и достаточно ли повторяется файл в результате упаковки, что значительно облегчает диагностику проблем при оптимизации проекта.
- Установить
$ npm i -D webpack-bundle-analyzer
- Настроить плагин
// 引入插件
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const config = {
// ...
plugins:[
// ...
// 配置插件
new BundleAnalyzerPlugin({
// analyzerMode: 'disabled', // 不启动展示打包报告的http服务器
// generateStatsFile: true, // 是否生成stats.json文件
})
],
};
- Изменить команду запуска
"scripts": {
// ...
"analyzer": "cross-env NODE_ENV=prod webpack --progress --mode production"
},
- выполнить команду компиляции
npm run analyzer
После того, как упаковка будет завершена, она автоматически начнется по адресуhttp://127.0.0.1:8888
Веб-сервис, вы можете увидеть адрес доступа
Если мы хотим только сохранить данные и не хотим запускать веб-службу, на данный момент мы можем добавить две конфигурации.
new BundleAnalyzerPlugin({
analyzerMode: 'disabled', // 不启动展示打包报告的http服务器
generateStatsFile: true, // 是否生成stats.json文件
})
Таким образом, при повторном выполнении упаковки будет создан только файл state.json.
2.2 Сжатие CSS
- Установить
optimize-css-assets-webpack-plugin
$ npm install -D optimize-css-assets-webpack-plugin
- Исправлять
webapck.config.js
настроить
// ...
// 压缩css
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
// ...
const config = {
// ...
optimization: {
minimize: true,
minimizer: [
// 添加 css 压缩配置
new OptimizeCssAssetsPlugin({}),
]
},
// ...
}
// ...
- Посмотреть результаты упаковки
2.3 Сжатие JS
Упаковка в производственной среде по умолчанию включает сжатие js, но когда мы вручную настраиваем
optimization
После выбора опции js больше не сжимается по умолчанию, и нам нужно настроить его вручную.
Поскольку webpack5 имеет встроенныйterser-webpack-pluginПлагин, поэтому нам не нужно устанавливать его повторно, просто укажите его напрямую, конкретная конфигурация выглядит следующим образом.
const TerserPlugin = require('terser-webpack-plugin');
const config = {
// ...
optimization: {
minimize: true, // 开启最小化
minimizer: [
// ...
new TerserPlugin({})
]
},
// ...
}
2.4 Очистите бесполезный CSS
purgecss-webpack-pluginИзвлеките CSS отдельно и очистите неиспользуемый CSS
- Установить плагин
$ npm i -D purgecss-webpack-plugin
- добавить конфигурацию
// ...
const PurgecssWebpackPlugin = require('purgecss-webpack-plugin')
const glob = require('glob'); // 文件匹配模式
// ...
function resolve(dir){
return path.join(__dirname, dir);
}
const PATHS = {
src: resolve('src')
}
const config = {
plugins:[ // 配置插件
// ...
new PurgecssPlugin({
paths: glob.sync(`${PATHS.src}/**/*`, {nodir: true})
}),
]
}
- index.html новый узел
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ITEM</title>
</head>
<body>
<p></p>
<!-- 使用字体图标文件 -->
<i class="iconfont icon-member"></i>
<div id="imgBox"></div>
<!-- 新增 div,设置 class 为 used -->
<div class="used"></div>
</body>
</html>
- Добавляем стили в sass.scss
.used {
width: 200px;
height: 200px;
background: #ccc;
}
.unused {
background: chocolate;
}
- Выполнить пакет
Мы можем видеть только.used
был сохранен
Как доказать, что это роль этого плагина? Вы можете увидеть это, закомментировав его и снова упаковав,.unused
Он также будет упакован, что доказывает...
2.5 Tree-shaking
Роль Tree-shaking заключается в удалении неиспользуемого кода для уменьшения размера пакета.
- webpack поддерживает его по умолчанию и должен быть установлен в .bablerc
model:false
, который может быть включен по умолчанию в производственной среде.
Узнайте больше о Tree-shaking, рекомендуем прочитать 👉🏻От прошлого к настоящему, рассказ о Tree-shaking
module.exports = {
presets: [
[
"@babel/preset-env",
{
module: false,
useBuiltIns: "entry",
corejs: "3.9.1",
targets: {
chrome: "58",
ie: "11",
},
},
],
],
plugins: [
["@babel/plugin-proposal-decorators", { legacy: true }],
["@babel/plugin-proposal-class-properties", { loose: true }],
]
};
2.6 Scope Hoisting
Scope Hoisting — это продвижение области действия. Принцип заключается в том, чтобы поместить несколько модулей в одну область действия и переименовать их, чтобы предотвратить конфликты имен.Таким образом, объявление функции и накладные расходы памяти могут быть уменьшены..
- Webpack поддерживает его по умолчанию, и он включен по умолчанию в производственной среде.
- Поддерживает только код es6
3. Оптимизируйте работу во время выполнения
Суть оптимизации во время выполнения заключается в повышении скорости загрузки первого экрана.
- Уменьшите размер загружаемых файлов в верхней части страницы и предварительно загрузите или загрузите файлы по запросу, которые не нужны в верхней части страницы.
3.1 Сегментация точки входа
Настройте несколько записей упаковки и многостраничную упаковку, но я не буду вводить здесь слишком много.
3.2 Конфигурация подпакета splitChunks
Оптимизация.splitChunks основана наSplitChunksPluginреализовано плагином
По умолчанию это влияет только на фрагменты по запросу, поскольку изменение начальных фрагментов влияет на теги сценария в HTML-файле проекта.
webpack автоматически разбивает чанки на основе следующих условий:
- Новые фрагменты могут быть общими, или модули из
node_modules
папка - Размер нового фрагмента больше 20 КБ (размер до min+gz)
- При загрузке чанков по запросу максимальное количество параллельных запросов меньше или равно 30
- При загрузке страницы инициализации максимальное количество одновременных запросов меньше или равно 30
- Введение в конфигурацию по умолчанию
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async', // 有效值为 `all`,`async` 和 `initial`
minSize: 20000, // 生成 chunk 的最小体积(≈ 20kb)
minRemainingSize: 0, // 确保拆分后剩余的最小 chunk 体积超过限制来避免大小为零的模块
minChunks: 1, // 拆分前必须共享模块的最小 chunks 数。
maxAsyncRequests: 30, // 最大的按需(异步)加载次数
maxInitialRequests: 30, // 打包后的入口文件加载时,还能同时加载js文件的数量(包括入口文件)
enforceSizeThreshold: 50000,
cacheGroups: { // 配置提取模块的方案
defaultVendors: {
test: /[\/]node_modules[\/]/,
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
};
- использовать в проекте
const config = {
//...
optimization: {
splitChunks: {
cacheGroups: { // 配置提取模块的方案
default: false,
styles: {
name: 'styles',
test: /\.(s?css|less|sass)$/,
chunks: 'all',
enforce: true,
priority: 10,
},
common: {
name: 'chunk-common',
chunks: 'all',
minChunks: 2,
maxInitialRequests: 5,
minSize: 0,
priority: 1,
enforce: true,
reuseExistingChunk: true,
},
vendors: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
chunks: 'all',
priority: 2,
enforce: true,
reuseExistingChunk: true,
},
// ... 根据不同项目再细化拆分内容
},
},
},
}
3.3 Ленивая загрузка кода
Для некоторых ресурсов, которые не нужны для загрузки на первом экране, мы можем реализовать отложенную загрузку, давайте немного разберемся 🌰
- Требования: Нажмите на картинку, чтобы добавить описание к картинке
1. Создайте новое описание изображения
desc.js
const ele = document.createElement('div')
ele.innerHTML = '我是图片描述'
module.exports = ele
2. Нажмите на картинку, чтобы ввести описание
index.js
import './main.css';
import './sass.scss'
import logo from '../public/avatar.png'
import '@/fonts/iconfont.css'
const a = 'Hello ITEM'
console.log(a)
const img = new Image()
img.src = logo
document.getElementById('imgBox').appendChild(img)
// 按需加载
img.addEventListener('click', () => {
import('./desc').then(({ default: element }) => {
console.log(element)
document.body.appendChild(element)
})
})
3. Проверьте эффект
- до щелчка
- После нажатия
3.4 предварительная выборка и предварительная загрузка
Выше мы использовали метод асинхронной загрузки, чтобы ввести описание изображения, но если файл, который необходимо загрузить асинхронно, относительно большой, его загрузка во время щелчка также повлияет на наш опыт.В настоящее время мы можем рассмотреть возможность использования предварительная выборка для предварительной выборки
3.4.1 prefetch
- prefetch(Предварительная выборка): извлекает ресурсы, когда браузер бездействует.
Измените приведенный выше код
// 按需加载
img.addEventListener('click', () => {
import( /* webpackPrefetch: true */ './desc').then(({ default: element }) => {
console.log(element)
document.body.appendChild(element)
})
})
3.4.2 preload
- preload(Предварительная загрузка): заранее загружать ключевые ресурсы, которые будут использоваться позже.
- ⚠️ Поскольку ресурсы будут тянуться заранее, если нет особой необходимости, используйте их с осторожностью.
Пример официального сайта:
import(/* webpackPreload: true */ 'ChartingLibrary');
4. Рукописный загрузчик
TODO
5. Рукописный плагин
TODO
Тайна исчезновения ежемесячного блогера
На самом деле я еще с августа планировал разобраться с вопросом по системе знаний webpack.Не ожидал, что на это уйдет несколько месяцев 😂
И это еще не конец 😅
Во избежание окончательного незаконченного конца статьи, я решил обновить ее по разделам.
В следующей статье будет добавлено:"Углубленный веб-пакет"
Общее содержание включает в себя:
- Отладка веб-пакета
- Процесс сборки веб-пакета
- Принцип горячего обновления (HRM)
- Представляем Tapabel, основную библиотеку Webpack
- принцип тряски дерева
- синтаксическое дерево babel и AST
Ставьте лайки, подписывайтесь, комментируйте, поддержите волну
Ваша поддержка является движущей силой для моего письма,Ставьте лайки, подписывайтесь, комментируйте, поддержите волну👍
Подпишитесь на мой официальный аккаунт:каменщик переднего края, чтобы вместе построить свод знаний