🔥【4D】Проанализировав вопросы интервью с webpack, создайте систему знаний webpack5.x

внешний интерфейс JavaScript Webpack
🔥【4D】Проанализировав вопросы интервью с webpack, создайте систему знаний webpack5.x

👨‍💻 Интервьюер:Участвовали ли вы в настройке Webpack в проекте вашей компании? Или вы внесли какие-либо оптимизации в конфигурацию?

👨‍🌾 Я:нет 😅

👨‍💻 Интервьюер:em...

В это время прозвучала фоновая музыка: "🎵 Я больше всего боюсь внезапной тишины в воздухе~"


На самом деле этоОчень искренний ответ!У многих действительно нет возможности участвовать в настройке Webpack в проекте, не говоря уже об оптимизации конфигурации.

Таким образом, легко получить следующие проблемы:

  1. Без дополнительных возможностей для практики люди склонны игнорировать ее изучение.
  2. Когда я хочу учиться, я не знаю с чего начать🤦‍♂️

В это время мы можем сначала взглянуть на то, что обычно спрашивают о Webpack во время интервью.

Зачем начинать с вопросов для интервью?

  1. Узнайте, какие знания, связанные с Webpack, конкретно ожидают от кандидатов
  2. Нам удобно фиксировать очки знаний изучения Webpack

Здесь я нашел несколько репрезентативных вопросов, попробуйте посмотреть, на сколько вы сможете ответить 😉

В: Какие загрузчики используются в конфигурации Webpack? Что оно делает?

В: Какие плагины используются в конфигурации Webpack? Что оно делает?

В: В чем разница между загрузчиком и плагином?

Q: Как написать Loader?Внести идею?

В: Как написать плагин?Внести идею?

Q: Была ли настроена оптимизация Webpack? Можете ли вы сказать это просто?

Q: Как оптимизировать производительность на уровне Webpack?

В: Что такое процесс упаковки Webpack?

В: Каков принцип реализации tree-shaking?

Вопрос. Как работает горячее обновление Webpack (HMR)?

В: Как плагин Babel работает в связке Webpack?

В: В чем сходство и различие между Webpack и Rollup?

В: Какие новые функции были обновлены в Webpack5?

как насчет этого? Эти вопросы в порядке?

Далее мы разбираем и обобщаем эти вопросы интервью и рисуем общую систему знаний

webpack.jpg

После того, как система знаний установлена, мы сосредоточимся на системе знаний и просто разделим ее на три уровня:

  1. Основание-- будем настраивать
  2. Передовой-- можно оптимизировать
  3. глубоко- понять принцип

Повышайте уровень, чтобы сражаться с монстрами 🦖, отвечайте на вопросы интервью Webpack разной глубины 🤓

Приходите, начнем прямо сейчас 🤜

1. Основы веб-пакета

В первой части, начиная с простого требования «квалифицированной настройки», сначала разберитесь с простой настройкой Webpack и вопросами собеседования, связанными с простой настройкой.

1. Простая конфигурация

Эта часть требует освоения:

  1. Каковы общие элементы конфигурации Webpack?
  2. Какие загрузчики обычно используются? Как настроить?
  3. Каковы общие плагины (Plugin)? Как настроить?
  4. Как настроить 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, мы можем это протестировать

  1. новый./src/index.jsфайл, напишите простой код
const a = 'Hello ITEM'
console.log(a)
module.exports = a;

Структура каталогов на данный момент

webpack_work                  
├─ src                
│  └─ index.js         
└─ package.json       
  1. бежать напрямуюnpx webpack, начать упаковку

image.png

После завершения упаковки видим в логе сообщение:The 'mode' option has not been set,...

Это значит, что мы не настроили режим, здесь нам напоминают его настроить

модель:Для параметра конфигурации режима скажите веб-пакету использовать встроенную оптимизацию соответствующего режима, значение по умолчанию равноproduction,а такжеdevelopment,none, их отличия заключаются в следующем

Опции описывать
development Режим разработки, более быстрая упаковка, сохранение шагов оптимизации кода
production В производственном режиме упаковка работает медленно, включена древовидная тряска и сжатый код.
none Не используйте параметры оптимизации по умолчанию

Как настроить? очень простой

  1. Просто укажите параметр режима в объекте конфигурации:
module.exports = {
  mode: 'development',
};
  1. Передано из аргументов CLI:
$ webpack --mode=development

1.3 Файлы конфигурации

Хотя пакетов конфигурации 0, на практике нам все равно нужно использовать файлы конфигурации для удовлетворения потребностей разных проектов.

  1. Создайте новый файл конфигурации в корневом путиwebpack.config.js

  2. Добавьте базовую информацию о конфигурации

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;
}
  1. Изменить конфигурацию записи
const path = require('path')

module.exports = {
  mode: 'development', // 模式
  entry: './src/main.css', // 打包入口地址
  output: {
    filename: 'bundle.css', // 输出文件名
    path: path.join(__dirname, 'dist') // 输出文件目录
  }
}

3. Запустите команду упаковки:npx webpack

image.png

Вот ошибка!

Это потому что:По умолчанию webpack поддерживает обработку файлов JS и JSON, а другие типы не могут быть обработаны, здесь необходимо использовать Loader для обработки разных типов файлов.

  1. Установитьcss-loaderдля обработки CSS
npn install css-loader -D
  1. Настройка модулей загрузки ресурсов
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 名称
      }
    ]
  }
}
  1. Повторно запустите команду пакетаnpx webpack

image.png

Эй, он готов к упаковке 😁

dist           
└─ bundle.css  # 打包得到的结果

image.png

Это попытка здесь, файл входа еще нужно изменить обратно./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реализовать

  1. Установить
$ npm install clean-webpack-plugin -D
  1. настроить
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 Различайте окружение

Должны быть разные потребности для локальной разработки и онлайн-развертывания.

Местная среда:

  • Нужны более быстрые сборки
  • Необходимо распечатать отладочную информацию
  • Требуется функция оперативной перезагрузки или горячей перезагрузки
  • Исходная карта необходима для облегчения поиска проблемы
  • ...

Производственная среда:

  • Нужен меньший размер пакета, сжатие кода + встряхивание дерева
  • требуется разделение кода
  • Нужно сжать размер изображения
  • ...

Для разных потребностей первое, что нужно сделать, это отличить окружающую среду

  1. Установите cross-env локально [адрес документа]
npm install cross-env -D
  1. Настроить команды запуска

Открыть./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"
  },
  1. Получить переменные среды в файле конфигурации 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 вы можете напрямую читать файл в соответствующем статическом каталоге без какого-либо перемещения файла, что экономит время и снижает нагрузку на производительность.

  1. запустить локальную службу
$ 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/

image.png

Затем доступhttp://localhost:8080/logo.png

image.png

Хорошо, без проблем 👌

1.9 Введение в CSS

Выше мы говорили об использовании css-loader для обработки css в Loader, но сам по себе css-loader не может загружать стили на страницы. В настоящее время нам нужно установить другой загрузчик стилей, чтобы завершить эту функцию.

style-loader — добавить обработанный css на страницу в виде тегов стиля

  1. Установитьstyle-loader [адрес документа]
npm install style-loader -D
  1. Настроить загрузчики
const config = {
  // ...
  module: { 
    rules: [
      {
        test: /\.css$/, //匹配所有的 css 文件
        use: ['style-loader','css-loader']
      }
    ]
  },
  // ...
}

⚠️Примечание:Порядок выполнения Loader фиксируется сзади наперед, то есть нажмитеcss-loader --> style-loaderпоследовательность выполнения

  1. Файл стиля цитирования

во входном файле./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;
}
  1. Перезапустите локальную службу и посетитеhttp://localhost:8080/

image.png

Этот стиль работает, продолжайте изменять стиль

body {
  margin: 10px auto;
  background: cyan;
  max-width: 800px;
  /* 新增 */
  font-size: 46px;
  font-weight: 600;
  color: white;
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
}

После сохранения стиль автоматически изменяется.

image.png

Основная логика 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

Попробуйте снова

image.png

Префикс добавляется автоматически 👏

если ты прав.browserslistrcЕсли вас интересуют эффекты различных конфигураций, вы можете использоватьautoprefixerСовершайте онлайн-конверсии, чтобы увидеть результаты

image.png

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 в качестве примера.

  1. Установить
$ npm install sass-loader -D
# 淘宝镜像
$ npm i node-sass --sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
  1. новый./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;
  }
}
  1. Импорт файлов Sass
import './main.css';
import './sass.scss' // 引入 Sass 文件


const a = 'Hello ITEM'
console.log(a)
module.exports = a;
  1. Изменить настройку
const config = {
   // ...
   rules: [
      {
        test: /\.(s[ac]|c)ss$/i, //匹配所有的 sass/scss/css 文件
        use: [
          'style-loader',
          'css-loader',
          'postcss-loader',
          'sass-loader', 
        ]
      },
    ]
  },
  // ...
}

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

image.png

успехов 👏

1.12 Отдельные файлы стилей

Впереди мы все зависим отstyle-loaderДобавляйте стили на страницу в виде тегов стилей

Однако чаще мы все надеемся, что его можно внедрить на страницу в виде CSS-файла.

  1. Установитьmini-css-extract-plugin
$ npm install mini-css-extract-plugin -D
  1. Исправлять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'
    }),
    // ...
  ]
}

// ...
  1. Посмотреть результаты упаковки
dist                    
├─ avatar.d4d42d52.png  
├─ bundle.js            
├─ index.html           
├─ logo.56482c77.png    
└─ main.3bcbae64.css # 生成的样式文件  

image.png

1.13 Файлы изображений и шрифтов

Хотя при настройке среды разработки выше мы можем установитьcontentBaseПерейдите непосредственно к чтению статических файлов класса изображений и посмотрите на использование следующих двух изображений.

  1. прямой импорт страницы
<!-- 本地可以访问,生产环境会找不到图片 -->
<img src="/logo.png" alt="">
  1. Введение фонового изображения
<div id="imgBox"></div>
/* ./src/main.css */
...
#imgBox {
  height: 400px;
  width: 400px;
  background: url('../public/logo.png');
  background-size: contain;
}

сообщит об ошибке напрямую

image.png

Так что на самом деле Webpack не может распознавать файлы изображений и нуждается в обработке при упаковке

Обычно используемые загрузчики для обработки файлов изображений включают:

Loader иллюстрировать
file-loader Решите проблему с введением изображения и скопируйте изображение в указанный каталог, по умолчанию dist
url-loader Решение зависит от файла-загрузчика.Когда изображение меньше предельного значения, изображение будет преобразовано в кодировку base64.Когда значение больше предельного значения, файл-загрузчик по-прежнему используется для копирования.
img-loader Сжать изображение
  1. Установитьfile-loader
npm install file-loader -D
  1. Изменить настройку
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)

Запускаем сервис, посмотрим на эффект

image.png

Отображение нормальное ✌️

Мы видим, что имя файла изображения было изменено, и значение хеш-функции было добавлено, а затем я взгляну на каталог пакета.

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

  1. Установитьurl-loader
$ npm install url-loader -D
  1. настроить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 

Упакуем и посмотрим на эффект

image.png

Очевидно, вы можете видеть, что файл logo.png был преобразован в base64 👌

Посмотрите на обработку файлов шрифтов

  1. Настроить файлы шрифтов

Во-первых, изiconfont.cnЗагрузите файл шрифта на локальный

image.png

В проекте создайте новый./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,
        } 
      }
    ]
  },
  // ...
}

Упакуйте его и посмотрите на эффект

image.png

Но в webpack5 встроен модуль обработки ресурсов,file-loaderа такжеurl-loaderнет необходимости устанавливать

1.14 Использование модулей ресурсов

webpack5 добавляет модуль ресурсов, который позволяет использовать файлы ресурсов (шрифты, значки и т. д.) без настройки дополнительных загрузчиков.

Модуль ресурсов поддерживает следующие четыре конфигурации:

  1. asset/resourceРазделите ресурсы на отдельные файлы и экспортируйте URL-адреса, как в предыдущей функции загрузчика файлов.
  2. asset/inlineЭкспортировать ресурс в виде dataUrl, аналогично функции предыдущего url-загрузчика, когда параметр меньше лимита.
  3. asset/sourceЭкспорт ресурсов в виде исходного кода.
  4. 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.

Здесь появится «самый красивый мальчик» - Бабель

  1. Бабель не настроен

Давайте напишем что-нибудь 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Просмотр упакованных результатов

image.png

image.png

Хотя мы все еще можем найти наш код, но он не интуитивно понятен, мы сначала устанавливаем режим наnone, упакованный в самом примитивном виде, а потом посмотрите на результаты упаковки

image.png

Запакованный код сильно не меняется, но адрес изображения заменяется.Дальше посмотрим как изменится запакованный результат после настройки babel

  1. Установить зависимости
$ npm install babel-loader @babel/core @babel/preset-env -D
  • babel-loaderЗагрузите код ES2015+ и преобразуйте его в ES5 с помощью Babel.
  • @babel/coreБазовый пакет, скомпилированный Babel
  • @babel/preset-envСкомпилированные Babel пресеты, которые можно рассматривать как расширенный набор плагинов Babel.
  1. Настройка пресетов 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'
              ],
            }
          }
        ]
      },
    // ...
    ]
  },
  //...
}
// ...

После завершения настройки запустите пакет

image.png

Только что написанный класс 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

Если интересно, можете узнать сами.Мы не будем здесь распространяться.Поговорим об использовании плагинов.

  1. Настройте плагин 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()

Выполнить пакет

image.png

Неудивительно, что не узнал 🙅🏻‍♀️

Как я могу его использовать? 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, который был преобразован в поддержку браузера

image.png

Точно так же мы можем использовать различные плагины в соответствии с нашими реальными потребностями.

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 Различия элементов конфигурации

  1. Для удобства сравнения их различий мысоздать новый проект
webpack_source_map                                             
├─ src                                      
│  ├─ Author.js                            
│  └─ index.js                               
├─ package.json                             
└─ webpack.config.js                        
  1. Открыть./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
  1. Открыть./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)
  1. Открыть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"
  }
}

  1. Открыть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Что внутри, цель этого шага — протестировать мультивходную упаковку

Цель преобразования в несколько входов - облегчить наше сравнение позже.

  1. Используйте отдельные записи упаковки для разных элементов конфигурации, откройте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`
      })
    ]
  }
}
  1. Ошибка кода моделирования
// ./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)
  1. попробуй упаковать

Сообщил неправильно!!

image.png

Выдается подсказка, что название режима 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'
]

Выполнить пакет снова

Ошибка все равно есть! ! затем изменить

image.png

Я проверил сообщение об ошибке, вероятность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 и открываем его в браузере

image.png

Затем мы анализируем один за другим

evalмодель:

  1. Сгенерировать код черезevalВыполнить 👇🏻

image.png

  1. @sourceURL

image.png

source-map

image.png

eval-source-map

  1. eval

image.pngdataUrl

image.png

image.png

eval-cheap-source-map

  1. eval
  2. dataUrl

eval-cheap-module-source-map

  1. eval
  2. dataUrl

image.png

inline-source-map

  1. dataUrl

image.png

source-mapтот же образец

hidden-source-mapмодель:

  1. Не видно эффекта SourceMap, но создается файл SourceMap

nosources-source-mapмодель:

  1. Вы можете увидеть, где возникает ошибка 👇🏻

image.png

  1. Но нет возможности реально соответствовать исходному коду

image.png

Далее немного подытожим:

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 Рекомендуемая конфигурация

  1. Местное развитие:

рекомендовать:eval-cheap-module-source-map

причина:

  • Не беда, если первый пакет локальной разработки будет медленным, потому чтоevalПричина в кеше, ребилд будет очень быстрым
  • В разработке мы не будем писать слишком долго каждую строчку кода, нам нужно только найти строчку, так что добавимcheap
  • Мы хотим иметь возможность находить ошибки в исходном коде, а не в запакованном, поэтому нам нужно добавитьmodele
  1. Производственная среда:

рекомендовать:(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, мы обращаемся к документации для настройки

  1. Сначала установите его
$ npm i -D speed-measure-webpack-plugin
  1. Измените наш файл конфигурации 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);
}
  1. выполнить упаковку

image.png

Сообщил об ошибке 🤦🏻‍♂️

Это обнажает один из недостатков использования этого плагина, а именно:

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

Здесь у нас естьmini-css-extract-pluginВыполните процесс понижения версии:^2.1.0 -> ^1.3.6

Переустановите зависимости и снова выполните упаковку

image.png

После понижения версии все равно выдает ошибку.Согласно подсказке,добавляем конфигурациюpublicPath: './'

output: {
    filename: 'bundle.js', // 输出文件名
    path: path.join(__dirname, 'dist'), // 输出文件目录
    publicPath: './'
  },

попытка один раз

image.png

Это сработало!

Примечание. Очень неэкономично понижать версию плагинов или изменять запись конфигурации, чтобы использовать трудоемкий анализ в 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 попытается разобрать модули по порядку слева направо в массиве, сконфигурированном расширениями.

нужно знать, это:

  1. Суффикс имени высокочастотного файла помещается впереди;
  2. После ручной настройки конфигурация по умолчанию будет перезаписана.

Если вы хотите сохранить конфигурацию по умолчанию, вы можете использовать...Оператор распространения представляет конфигурацию по умолчанию, например.

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 вместо упаковки:

  1. Ввести ссылки
<script
  src="https://code.jquery.com/jquery-3.1.0.js"
  integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
  crossorigin="anonymous"
></script>
  1. настроить внешние
const config = {
  //...
  externals: {
    jquery: 'jQuery',
  },
};
  1. Используйте 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 });

Следующие примеры демонстрируют несколько вариантов использования этого плагина.

  1. Установить плагин момента (библиотека обработки времени)
$ npm i -S moment
  1. Настроить плагин игнорирования
// 引入 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).

  1. Установить
$ npm i -D  thread-loader
  1. настроить
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
  1. Установить
$ npm i -D cache-loader
  1. Настроить загрузчик кеша
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Мы можем интуитивно видеть размер файла, зависимости каждого модуля и достаточно ли повторяется файл в результате упаковки, что значительно облегчает диагностику проблем при оптимизации проекта.

  1. Установить
$ npm i -D webpack-bundle-analyzer
  1. Настроить плагин
// 引入插件
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin


const config = {
  // ...
  plugins:[ 
    // ...
    // 配置插件 
    new BundleAnalyzerPlugin({
      // analyzerMode: 'disabled',  // 不启动展示打包报告的http服务器
      // generateStatsFile: true, // 是否生成stats.json文件
    })
  ],
};
  1. Изменить команду запуска
 "scripts": {
    // ...
    "analyzer": "cross-env NODE_ENV=prod webpack --progress --mode production"
  },
  1. выполнить команду компиляцииnpm run analyzer

После того, как упаковка будет завершена, она автоматически начнется по адресуhttp://127.0.0.1:8888Веб-сервис, вы можете увидеть адрес доступа

image.png

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

new BundleAnalyzerPlugin({
   analyzerMode: 'disabled',  // 不启动展示打包报告的http服务器
   generateStatsFile: true, // 是否生成stats.json文件
})

Таким образом, при повторном выполнении упаковки будет создан только файл state.json.

2.2 Сжатие CSS

  1. Установитьoptimize-css-assets-webpack-plugin
$ npm install -D optimize-css-assets-webpack-plugin 
  1. Исправлятьwebapck.config.jsнастроить
// ...
// 压缩css
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
// ...

const config = {
  // ...
  optimization: {
    minimize: true,
    minimizer: [
      // 添加 css 压缩配置
      new OptimizeCssAssetsPlugin({}),
    ]
  },
 // ...
}

// ...
  1. Посмотреть результаты упаковки

image.png

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

  1. Установить плагин
$ npm i -D purgecss-webpack-plugin
  1. добавить конфигурацию
// ...
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})
    }),
  ]
}

  1. 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>

  1. Добавляем стили в sass.scss
.used {
  width: 200px;
  height: 200px;
  background: #ccc;
}

.unused {
  background: chocolate;
}
  1. Выполнить пакет

image.png

Мы можем видеть только.usedбыл сохранен

Как доказать, что это роль этого плагина? Вы можете увидеть это, закомментировав его и снова упаковав,.unusedОн также будет упакован, что доказывает...

2.5 Tree-shaking

Роль Tree-shaking заключается в удалении неиспользуемого кода для уменьшения размера пакета.

  • webpack поддерживает его по умолчанию и должен быть установлен в .bablercmodel: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
  1. Введение в конфигурацию по умолчанию
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,
        },
      },
    },
  },
};
  1. использовать в проекте
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. Проверьте эффект

  • до щелчка

image.png

image.png

  • После нажатия

image.png

image.png

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

Ставьте лайки, подписывайтесь, комментируйте, поддержите волну

Ваша поддержка является движущей силой для моего письма,Ставьте лайки, подписывайтесь, комментируйте, поддержите волну👍

Подпишитесь на мой официальный аккаунт:каменщик переднего края, чтобы вместе построить свод знаний

qrcode_for_gh_ea885e0b59be_258 (1).jpg

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