Построчно вручную стучать по конфигурации webpack4

Webpack

код: гитхаб


1. webpack4 -- базовая конфигурация

1. Инициализируйте конфигурацию

mkdir webpack4
cd webpack4
mkdir demo1
cd demo1
npm init -y 或 npm init

Структура каталогов

webpack4
├── webpack4/demo1
│   └── webpack4/demo1/package.json
└── webpack4/README.md

  • Установить веб-пакетnpm install webpack --save-dev

  • Установите указанную версию веб-пакетаnpm install --save-dev webpack@<version>

  • версия webpack 4+, также необходимо установить webpack-clinpm install webpack-cli --save-dev

  • npx webpack -v: просмотреть версию веб-пакета

  • npx webpack-cli -v: Посмотреть версию webpack-cli

Рекомендуется установить webpack и webpack-cli локально На момент написания этого блога последняя версия webpack: 4.30.0, которая также используется в этой статье для изучения webpack4.

Создайте новый каталог src в каталоге demo1 и создайте новый index.js в каталоге src.

mkdir src 
cd src
touch index.js

структура каталогов demo1

demo1
├── demo1/package.json
├── demo1/package-lock.json
└── demo1/src
    └── demo1/src/index.js

Добавьте код в index.js, например:

//index.js

let demo='webpack4'
console.log(demo)

webpack4 может быть упакован с нулевой конфигурацией, а webpack4 по умолчанию упаковывает файл index.js в каталог src. беги сейчасnpx webapck, вы можете увидеть каталог dist в каталоге demo1, в каталоге dist есть файл main.js, это запакованный файл, откройте поиск, чтобы увидетьconsole.log(demo), что указывает на то, что index.js упакован в main.js.

2. Простая настройка webpack4

существуетdemo1новый каталогwebpackконфигурационный файлwebpack.config.js

настроитьwebpack--webpack.config.js
const path = require('path')

module.exports={
  //mode development: 开发环境  production:生产环境
  mode: 'development', 
  //entry 入口文件配置  
  entry: {
    index: './src/index.js'
  },
  //打包完成后文件输出位置配置
  output: {
    //filename 设置打包后文件的名字
    //如果不设置filename,则文件的名字跟入口文件路径的属性名一样
    filename: 'bundle.js',
    //path 设置打包完成后文件输出路径
    path: path.resolve(__dirname,'dist')
  }
}

бегатьnpx webpackЗаказnpx webpackЭквивалентноnpx webpack --config webpack.config.js

когдаwebapckФайл конфигурации называетсяwebpack.config.jsможно опустить, когда--config *.js, выполнить напрямуюnpx webpackОК, иначе выполнитьnpx webpack --config 配置文件名.

Смотрите, что в каталоге dist естьbundle.js, указывая на то, что конфигурация веб-пакета верна.

существуетpackage.jsonнастроить «скрипт» в

"scripts": {
    "build": "webpack"
  }

Добавить к"build": "webpack",бегатьnpm run buildЭффект эквивалентен выполнениюnpx webpackЗаказ.

Настройте объект модуля webpack.config.js

Использование загрузчика
file-loaderиспользование

Установитьfile-loader npm i file-loader --save-dev

webpack.config.js

const path = require('path')

module.exports={
  //mode development: 开发环境  production:生产环境
  mode: 'development', 
  //entry 入口文件配置  
  entry: {
    index: './src/index.js'
  },
  //打包完成后文件输出位置配置
  output: {
    //filename 设置打包后文件的名字
    //如果不设置filename,则文件的名字跟入口文件路径的属性名一样
    filename: 'bundle.js',
    //path 设置打包完成后文件输出路径
    path: path.resolve(__dirname,'dist')
  },
  module: {
    rules:[
      {
        test: /\.(png|jpg|gif)$/,
        use: {
          loader: 'file-loader',
          options: {
            name: '[name].[ext]', //对打包后的图片命名
            outputPath: 'images/' //打包后图片输出的位置 dist\images
          }
        }
      }
    ]
  }
}

Создайте новую папку с изображениями в каталоге src для хранения изображений.

Изменить index.js

//index.js

//import导入图片
import image from './images/11.png'

let img=new Image()
img.src=image
document.body.append(img)

бегатьnpm run buildСтруктура каталогов выглядит следующим образом

demo1
├── demo1/dist
│   ├── demo1/dist/bundle.js
│   ├── demo1/dist/images
│   │   └── demo1/dist/images/11.png
│   └── demo1/dist/index.html
├── demo1/package.json
├── demo1/package-lock.json
├── demo1/src
│   ├── demo1/src/images
│   │   └── demo1/src/images/11.png
│   └── demo1/src/index.js
└── demo1/webpack.config.js

Каталог изображений и изображения появляются в каталоге dist, создайте index.html, импортируйте файл js и откройте его в браузере, чтобы увидеть изображения.

url-loaderиспользование

url-loaderУстановитьnpm i url-loader -D

url-loaderРоль очень похожа на роль «загрузчика файлов».

webpack.config.js

  module: {
    rules:[
     {
        test: /\.(png|jpg|gif)$/,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name].[ext]', //对打包后的图片命名
            outputPath: 'images/', //打包后图片放的位置dist\images
            limit: 20480
            //1024 == 1kb  
            //小于20kb时打包成base64编码的图片否则单独打包成图片
          }
        }
      }
    ]
  }
}

limitАтрибут: когда размер изображения больше, чем значение атрибута, оно упаковывается как изображение и выводится в каталог изображений, в противном случае оно упаковывается в изображение в кодировке base64 и внедряется в bundle.js.

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

правильноcssа такжеscssупаковка

Установите соответствующий загрузчикnpm i css-loader style-loader -D npm i node-sass sass-loader -D npm i postcss-loader -D npm i autoprefixer -D

postcss-loaderа такжеautoprefixerМожет автоматически добавлять префикс с использованием процесса упаковки в

Создайте новый в корневом каталоге demo1postcss.config.js, конфигурация следующая

//postcss.config.js
module.exports={
  plugins: [
    require('autoprefixer')
  ]
}

существуетwebpack.config.jsдокументmodule.rulesДобавить конфигурацию в массив

module:{
  rules:[
    {
      test: /\.css$/,
       use:[
         'style-loader',
         'css-loader',
         'postcss-loader'  
         //加前缀  npm i autoprefixer -D
         //在项目根目录下配置postcss.config.js文件
       ]
     },
     {
        test: /\.scss$/,
        use:[
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              //importLoaders
              //用于配置css-loader作用于@import的资源之前有多少个loader先作用于@import的资源
            }
          },
          'postcss-loader',
          'sass-loader'
        ]
      }
  ]
}

существуетdemo1изsrcпод новымcssпапка, вcssновая папкаstyle.cssа такжеindex.scssдокумент.

index.scss

body{
  border: 1px solid red;
  width: 300px;
  height: 300px;
  img{
    width: 100px;
    height: 100px;
    border-radius: 10%;
    transform: translate(100px,100px);
  }
}

style.css

body{
  border-radius: 10%;
}

index.js

//index.js

import image from './images/11.png'
import './style.css'
import './index.scss'

let img=new Image()
img.src=image
document.body.append(img)

бегатьnpm run build, создайте новый index.html в каталоге dist, импортируйте файл js и откройте его в браузере, чтобы увидеть эффект, указывающий на то, что упаковка выполнена успешно.

модульность css

Модульность CSS, чтобы избежать взаимодействия между стилями страницы существуетwebpack.config.jsсерединаcss-loaderДобавить кmodules: true

//webpack.config.js

module:{
  rules: [
      {
        test: /\.css$/,
        use:[
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true
            }
          },
          'postcss-loader'  
          //加前缀  npm i autoprefixer -D
          //在项目根目录下配置postcss.config.js文件
        ]
      },
      {
        test: /\.scss$/,
        use:[
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              //importLoaders
              //用于配置css-loader作用于@import的资源之前有多少个loader先作用于@import的资源
              modules: true //加载css模块化打包,避免样式文件之间相互影响
            }
          },
          'postcss-loader',
          'sass-loader'
        ]
      }
  ]
}

Изменить index.js .imgЭто имя класса, которое нужно заранее прописать в файле стилей.

//index.js

import image from './images/11.png'
import style from './css/style.css'
// import style from './css/index.scss'

let img=new Image()
img.src=image

//style.img .img是scss文件中写好的类名
img.classList.add(style.img)

document.body.append(img)

index.scss

body{
  border: 1px solid red;
  width: 300px;
  height: 300px;
  img{
    width: 100px;
    height: 100px;
    border-radius: 10%;
    transform: translate(100px,100px);
  }
  .img{
    border: 10px solid royalblue;
  }
}

style.css

body{
  border-radius: 10%;
}
body .img{
  border: 10px solid yellow;
}

результат

Вы можете видеть, что класс добавлен, а имя класса представляет собой строку более сложных строк, чтобы этот стиль не влиял на другие элементы.


2. Далее настраиваем webpack4, удобнее изучить webpack4

Эта часть в основном предназначена для того, чтобы научиться использоватьhtml-webpack-pluginа такжеclean-webpack-pluginПлагины, в основном для того, чтобы научиться настраиватьdevServerА также использовать функцию горячей замены модуля в webpack.

Первый вwebpack4новый каталогdemo2Папка будетdemo1Скопируйте все в каталоге вdemo2середина

В предыдущей части мы вручную создали index.html в каталоге dist, чтобы импортировать js-файлы для просмотра результатов упаковки, что было бы очень хлопотно. мы можем использоватьhtml-webpack-pluginЧтобы автоматически создавать index.html и автоматически импортировать упакованные файлы, вы можете напрямую открыть полученный HTML-файл, чтобы увидеть структуру упаковки.

1.html-webpack-pluginиспользование

Установитьnpm i html-webpack-plugin -D

существуетwebpack.config.jsНастройте элемент конфигурации плагинов в

const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')

module.exports={
  //mode development: 开发环境  production:生产环境
  mode: 'development', 
  //entry 入口文件配置  
  entry: {
    index: './src/index.js'
  },
  //打包完成后文件输出位置配置
  output: {
    //filename 设置打包后文件的名字
    //如果不设置filename,则文件的名字跟入口文件路径的属性名一样
    filename: 'bundle.js',
    //path 设置打包完成后文件输出路径
    path: path.resolve(__dirname,'dist')
  },
  module: { },
  plugins: [
    new htmlWebpackPlugin({
      template: './index.html'
    })
  ]
}

Создайте новый index.html в каталоге demo2 в качестве шаблона.

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>模板</title>
</head>
<body>
  <div id="root"></div>
<script type="text/javascript" src="bundle.js"></script></body>
</html>

бегатьnpm run build, вы можете видеть, что index.html автоматически создается в каталоге dist, и файлы js также автоматически импортируются

2.clean-webpack-pluginиспользование

Каждый раз, когда вы упаковываете сгенерированный каталог dist, если вы изменяете код один раз, вам придется один раз удалить каталог dist, что очень хлопотно.clean-webpack-pluginАвтоматически очищать каталог dist перед каждой упаковкой.

Установитьnpm i clean-webpack-plugin -D

существуетwebpack.config.jsизpluginsКонфигурация выглядит следующим образом

const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
const cleanWebpackPlugin = require('clean-webpack-plugin')

module.exports={
  //mode development: 开发环境  production:生产环境
  mode: 'development', 
  //entry 入口文件配置  
  entry: {
    index: './src/index.js'
  },
  //打包完成后文件输出位置配置
  output: {
    //filename 设置打包后文件的名字
    //如果不设置filename,则文件的名字跟入口文件路径的属性名一样
    filename: 'bundle.js',
    //path 设置打包完成后文件输出路径
    path: path.resolve(__dirname,'dist')
  },
  module: { },
  plugins: [
    new htmlWebpackPlugin({
      template: './index.html'
    }),
   new cleanWebpackPlugin()
  ]
}

бегатьnpm run build, можете проверить сами, файлы в каталоге dist будут удаляться перед каждой упаковкой.

3.entryа такжеoutputМноговходовая конфигурация

module.exports={
  //mode development: 开发环境 production:生产环境
  mode: 'development', 
  //entry 入口文件配置  
  entry: {
    index: './src/index.js',
    main: './src/index.js'
  },
  //打包完成后文件输出位置配置
  output: {
    //filename 设置打包后文件的名字
    //如果不设置filename,则文件的名字跟入口文件路径的属性名一样
    // 占位符
    filename: '[name].js',
    //path 设置打包完成后文件输出路径
    path: path.resolve(__dirname,'dist')
  },
}

Когда есть несколько записей, их необходимо изменить.filenameСтоимость недвижимости[name].js

бегатьnpm run build, он будет создан в каталоге distindex.jsа такжеmain.js

4. Конфигурацияdevtool

Devtool определяет отношение сопоставления между исходным кодом и упакованным кодом, что удобно для отладки кода.

Рекомендуемая среда разработки: Cheap-module-eval-source-map Рекомендуется для производственной среды: Cheap-module-source-map

Для конкретного содержания devtool, пожалуйста, обратитесь к:Документация: devtool

module.exports={
  devtool: 'cheap-module-eval-source-map',
  //开发环境推荐: cheap-module-eval-source-map
  //生产环境推荐: cheap-module-source-map
}

5. КонфигурацияdevServer

Документация: devServer

Установитьwebpack-dev-server npm i webpack-dev-server -D

Добавьте следующее в webpack.config.js
module.exports={
  devServer: {
    contentBase: './dist',
    // open: true, //自动打开浏览器
    // port: 8080, //默认8080
  }
}

Исправлятьpackage.jsonизscript,Добавить к"start": "webpack-dev-server"

 "scripts": {
    "start": "webpack-dev-server"
  },

воплощать в жизньnpm run startПосле открытия браузера вы можете увидеть эффект, когда мы изменим код, страница снова обновится.

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

6. Включите функцию горячей замены модулей webpack.

Сначала измените index.js
import './css/style.css'

var btn = document.createElement('button')
btn.innerHTML='新增'
document.body.appendChild(btn)

btn.onclick=function(){
  var div=document.createElement('div')
  div.innerHTML='items'
  document.body.appendChild(div)
}
Измените style.css и удалите index.scss.
//style.css
body{
  background: yellow;
}
div:nth-of-type(odd){
  background: chartreuse;
  font-size: 18px;
}
в webpack.config.js

Представьте веб-пакет:const webpack=require('webpack')Добавьте следующее содержимое:

const webpack=require('webpack')
module.exports={
  plugins: [
    new webpack.HotModuleReplacementPlugin() //启用HMR
  ],
  devServer: {
    contentBase: './dist',
    // open: true, //自动打开浏览器
    // port: 8080,
    hot: true,  //启用webpack的热模块替换功能
    hotOnly: true
    //devServer.hot在没有页面刷新的情况下启用热模块替换作为构建失败时的后备
  }
}

hot:trueвключитьHotModuleReplacementPlugin(ХМР)

воплощать в жизньnpm run start, После открытия браузера измените цвет фона div, изменится только измененное место, но страница не обновится.

Создайте новый number.js в каталоге src demo2.

number.js
var number=function(){
  var div=document.createElement('div')
  div.setAttribute("id","number")
  div.innerHTML=103
  document.body.appendChild(div)
}

export default number

Изменить index.js

import number from './number'
number()

бегатьnpm run start, откройте его в браузере, чтобы увидеть результат, а затем измените содержимое в number.js, но страница не отображает измененное содержимое

Это связано с тем, что реализация горячей замены модуля немного отличается при импорте js-файлов.

Для достижения эффекта горячей замены модуля в js необходим код if(module.hot){}, иначе даже при изменении кода страница не обновится, а модифицированное место будет изменено на странице.

Стиль CSS, потому что CSS-погрузчик уже реализовал часть If (Module.hot) {}, нет необходимости реализовывать эту часть отдельно.

Измените index.js еще раз

import number from './number'
number()

if(module.hot){
  module.hot.accept('./number.js',function(){
    number()
    document.body.removeChild(document.getElementById('number'))
  })
}

бегатьnpm run start, откройте его в браузере, чтобы увидеть результат, затем измените содержимое в number.js и обнаружите, что на странице отображается измененное содержимое.


3. Используйте Babel для обработки файлов js

Babel— это широко используемый транскодер, который может преобразовывать код ES6 в код ES5 для выполнения в существующих средах.

Babel разделен на три этапа: анализ, преобразование и генерация.

Сам Babel не имеет никакой функции преобразования, он разлагает функцию преобразования на один за другимpluginв. Поэтому, когда мы не настраиваем никаких плагинов, послеBabelВыходной код такой же, как и входной.

Использование плагина Babel

  1. Добавьте имя плагина в файл конфигурации: создайте в корневом каталоге проекта.babelrcфайл конфигурации илиwebapck.config.jsконфигурации, обычно в.babelrcв конфигурации.

  2. Установить с помощью npm install xxx

Конфигурационный файл Babel.babelrc, хранящийся в корневом каталоге проекта. Первым шагом в использовании Babel является настройка этого файла.

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

{
  "presets": [],
  "plugins": []
}

Краткое введение в Babel

preset

Пресет — это набор плагинов.@babel/preset-envКоллекция плагинов, содержащая все переводы с ES6 на ES5.

core-js

Преобразование некоторых встроенных классов (Promise, Symbols и т. д.) и статических методов (Array.from и т. д.).

@babel/core

Он существует как ядро ​​Babel, и все основные API Babel находятся в этом модуле.

babel-loader

babel-loaderИспользуется в webpack, это коммуникационный мост между webpack и Babel.

Введение в @babel/polyfill

@babel/preset-envПереводить только по умолчаниюjsсинтаксис без транспиляции новогоAPI,НапримерIterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promiseи другие глобальные объекты, а также некоторые методы, определенные для глобального объекта (например,Object.assign) Не будет переведен. Тогда вы должны использовать@babel/polyfill(интегрировано внутриcore-jsа такжеregenerator).

При использовании увеличивать перед запуском всего кодаimport "@babel/polyfill"

или вwebpack.config.jsВходная конфигурация

module.exports = {
  entry: ["@babel/polyfill", "./app/js"],
}

Поэтому должен@babel/polyfillтак какdependenciesвместоdevDependencies

У @babel/polyfill есть два основных недостатка:

1. Используйте@babel/polyfillНеобходимо выполнить некоторую дополнительную настройку, и пакет будет введен по мере необходимости, в противном случае он будет@babel/polyfillВнедрение всего в код приведет к очень большому пакету.

2.@babel/polyfillзагрязняет глобальные переменные.

Babel7Основное изменение заключается в том, чтоnpm packageизменить имя, положить всеbabel-*переименован в@babel/*,Например:

  • babel-polyfillпереименован в@babel/polyfill
  • babel-preset-envпереименован в@babel/preset-env

Использование Babel в веб-пакете

Сначала осуществите перевод грамматики ES6

Создайте новую папку demo3 в каталоге webpack4 и скопируйте все из каталога demo2 в demo3.

Установитьbabel-loader、 @babel/core、@babel/preset-env

  • npm i babel-loader -D
  • npm i @babel/core -D
  • npm i @babel/preset-env -D

babel-loader@8необходимо установить@babel/core7.xВерсия.

Настроено в webpack.config.js

module.exports={
  module: {
    rules:[
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use:{
          loader: 'babel-loader',         
          options:{
            presets: [
              ["@babel/preset-env",{
                //targets:表示编译出的代码想要支持的浏览器版本
                targets: {
                  chrome: "67"                 
                }
              }]
            ]
          }
        }
      }
    ]
  }
}

воплощать в жизньnpm run buildилиnpx webpackВы можете увидеть упакованные файлы в каталоге dist, но переведен только синтаксис ES6, а новый API ES6 не переведен, поэтому нам нужно настроить@babel/polyfillрешить эту проблему.

Установить@babel/polyfill npm i @babel/polyfill --save

существуетindex.jsвведен в@babel/polyfill

index.js

//index.js

import '@babel/polyfill'

let arr=[
  new Promise(()=>{}),
  new Promise(()=>{}),
  2
]

arr.map((item)=>{
  console.log(item)
})

представлять@babel/polyfillРаньше размер main.js составлял 29,5 КБ.

представлять@babel/polyfillПосле размера 1 МБ Main.js

Примечание. Приведенные выше сравнения не учитываютtargetsВ случае с этим вариантом, поскольку почти все браузеры поддерживают ES6, в данном случае@babel/preset-envКод не будет обработан.

Это потому, что@babel/polyfillРеализация всех API внедряется в файл пакета, но многие из API не используются в коде, поэтому нам нужно изменить конфигурацию и ввести соответствующие API по мере необходимости.

Изменить конфигурацию webpack.config.js

Добавить к"useBuiltIns": "usage"Позже необходимо установитьcore-js@2, и добавить"corejs": 2Элемент конфигурации, в настоящее время существует множество параметров конфигурации, вам необходимо создать новый в корневом каталоге проекта..babelrcфайл, настроить в этом файле.

.babelrcКонфигурация выглядит следующим образом:

  • "useBuiltIns"Стоимость свойства"usage"автоматически вводится, когда@babel/polyfill, вы должны убедиться, что он был установлен@babel/polyfill

  • "useBuiltIns"Стоимость свойства"usage", вам нужно добавить"corejs": 2Элемент конфигурации, иначе будет сообщено об ошибке, и потребуется установкаcore-js

сначала удалитьindex.jsсерединаimport '@babel/polyfill'

Установитьcore-js npm i --save core-js@2илиnpm i --save core-js@3

{
  "presets": [["@babel/preset-env",{
    "useBuiltIns": "usage", //不需要把polly都打包到代码中,根据代码按需转译
    // core-js@3和core-js@2二选一
    //"corejs": 3,  //npm i --save core-js@3
    "corejs": 2  //npm i --save core-js@2
  }]]
}

Исправлятьwebpack.config.js,удалятьoptionsобъект

module.exports={
  module: {
    rules:[
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      }
    ]
  }
}

воплощать в жизньnpm run build, размер упакованного файла165KB

Однако он не подходит для использования при разработке библиотек классов или сторонних модулей.@babel/polyfill, поэтому следующее использование@babel/plugin-transform-runtimeДля решения этой проблемы.

Использование @babel/plugin-transform-runtime, @babel/runtime и @babel/runtime-corejs2

@babel/runtime-corejs2: содержитBabel modular runtime helpersа такжеregenerator-runtimeтак же какcore-jsбиблиотека.

@babel/runtime: содержитBabel modular runtime helpersа такжеregenerator-runtimeбиблиотека.

в элементе конфигурацииcorejsЗначение свойства по умолчанию равноfalse, при необходимостиPromiseЖдатьAPIДля перевода необходимо установить значение атрибута в2и установить@babel/runtime-corejs2

Установить:

  • npm i @babel/plugin-transform-runtime -D
  • npm i --save @babel/runtime
  • npm i --save @babel/runtime-corejs2

Измените файл .babelrc

{
  "plugins": [
    ["@babel/plugin-transform-runtime",{
      "helpers": true,
      "regenerator": true,
      "useESModules": false,
      "corejs": 2
    }]
  ]
}

мы кладемpresetsЭлемент конфигурации удаляется, а затемnpm run buildупакованный, неупакованныйmain.jsПосмотреть, хотя переводPromise, но новый синтаксис ES6 не переносится, например:letне переведено наvar.

Так что еще нужно настроитьpresets,потому что"@babel/preset-env"Содержит плагины, переводящие весь синтаксис ES6 в ES5.

Снова измените файл .babelrc

{
  "presets": ["@babel/preset-env"],
  "plugins": [
    ["@babel/plugin-transform-runtime",{
      "helpers": true,
      "regenerator": true,
      "useESModules": false,
      "corejs": 2
    }]
  ]
}

Добавить кpresetsэлемент конфигурации, затемnpm run buildупакованный, неупакованныйmain.jsвижу-вижуletа стрелочные функции транспилируются в синтаксис ES5. 


Четыре,Tree Shakingиспользовать

Сначала создайте новую папку demo4 в каталоге webpack4 и скопируйте все содержимое каталога demo3 в demo4.

Tree Shakingможно использовать для удаленияJavaScriptМертвый код, который не работает. он зависит от статикиES6модульный синтаксис, например черезimportа такжеexportИмпорт и экспорт.

Следует отметить, чтоTree ShakingПредпосылкой нормальной работы являетсяJavaScriptкод должен использоватьES6модульный синтаксис, потому чтоES6Модульный синтаксис является статическим, что позволяетWebpackЧто можно легко проанализироватьexportодеялоimportпроходить.

Следующая настройкаWebpackПозволятьTree Shakingвступить в силу

webpack4Модульные операторы ES6 сохраняются по умолчанию и не преобразуются Babel. Исправлять.babelrcДокумент выглядит следующим образом:

//.babelrc

{
   "presets": [["@babel/preset-env",{
      "useBuiltIns": "usage",
      "corejs": 2,
      "modules":false //关闭 Babel 的模块转换功能,保留原本的 ES6 模块化语法
      //默认是auto,取值还可以是 amd, umd, systemjs, commonjs,auto等
   }]]
}

Исправлятьwebapck.config.js,Добавить к

optimization: {
  usedExports: true
}

прибытьmodule.exports{}середина

module.exports={
  mode: 'development',
  optimization: {
  //开发坏境使用tree shaking时加usedExports: true
    usedExports: true
  },
}

еще нужно пройтиpackage.jsonиз"sideEffects"Атрибут, указывающий веб-пакету, какие модули можно игнорировать, если не установлено значениеfalse, чтобы сообщить веб-пакету, что он может безопасно удалить неиспользуемыеexport.

Исправлятьpackage.json

{
  "name": "your-project",
  "sideEffects": false
}

Создайте новый math.js в src под demo4

index.js

//tree shaking import export
import {cube} from './math.js'

let component = () => {
  let element = document.createElement('pre')
  element.innerHTML = [
    'Hello webpack!',
    '2 cubed is equal to ' + cube(2)
  ].join('\n\n');
  console.log(cube)
  
  return element;
}
document.body.appendChild(component());

math.js

export let square= (x) => {
  console.log(x)
  return x * x;
}

export let cube = (x) => {
  console.log(x)
  return x * x * x;
}

бегатьnpm run build, затем откройте упакованный файл js: main.js, чтобы найти следующий текст

/*!*********************!*\
   !*** ./src/math.js ***!
   \*********************/
 /*! exports provided: square, cube */
 /*! exports used: cube */
 /***/

Из приведенного выше текста видно, чтоTree ShakingВступает в силу, но в среде разработки бесполезный код не удаляется, потому что код нужно отлаживать в среде разработки.

Мы определили «мертвый код», который необходимо удалить, однако речь идет не только о его поиске, но и об удалении. Для этого нам нужноmodeПараметры конфигурации установлены наproduction, удалить объект оптимизации, изменитьdevtoolпараметры конфигурации

webpack.config.js

module.exports = {
  mode: 'production',
  devtool: 'cheap-module-source-map'
}

бегатьnpm run build, вы можете увидеть, что бесполезный код был удален путем просмотра результатов упаковки.


Пятерки,Develomentа такжеProductionКонфигурация для разных сред

Создайте новый demo5 в webpack4 и скопируйте все файлы из demo4 в demo5.

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

Создайте новую папку сборки в корневом каталоге проекта, а затем создайте новую в папке сборки.webpack.dev.js,webpack.prod.jsа такжеwebpack.base.jsтри файла

webpack.dev.js: это среда разработкиwebpack.prod.js: производственная средаwebpack.base.js: конфигурация, используемая как в среде разработки, так и в производственной среде.

Комбинация этих файлов зависит от подключаемого модуля «webpack-merge».

Установитьnpm i webpack-merge -D

webpack.dev.js

//webpack.dev.js

const webpack=require('webpack')
const merge = require('webpack-merge')
const baseConfig=require('./webpack.base')

const devConfig={
  mode: 'development', 
  devtool: 'cheap-module-eval-source-map',
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ],
  optimization: {
    usedExports: true
  },
  devServer: {
    contentBase: './dist',
    // open: true, //自动打开浏览器
    // port: 8080,
    hot: true, //启用webpack的热模块替换功能
    //hotOnly: true
    //devServer.hot在没有页面刷新的情况下启用热模块替换作为构建失败时的后备
  }
}

module.exports=merge(baseConfig,devConfig)

webapck.prod.js

//webapck.prod.js

const merge = require('webpack-merge')
const baseConfig=require('./webpack.base')

const prodConfig={
  mode: 'production', 
  devtool: 'cheap-module-source-map'
}

module.exports=merge(baseConfig,prodConfig)

Но эти два документа есть много дублированного кода, новыйwebpack.base.js

//webpack.base.js

const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
const cleanWebpackPlugin = require('clean-webpack-plugin')

module.exports={
  entry: {
    main: './src/index.js'
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname,'dist')
  },
  module: {
    rules:[
      {
        test: /\.(png|jpg|gif)$/,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name].[ext]', 
            outputPath: 'images/', 
            limit: 2048           
          }
        }
      },
      {
        test: /\.css$/,
        use:[
          'style-loader',
          'css-loader',
          'postcss-loader' 
        ]
      },
      {
        test: /\.scss$/,
        use:[
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              modules: true 
            }
          },
          'sass-loader',
          'postcss-loader'
        ]
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      }
    ]
  },
  plugins: [
    new htmlWebpackPlugin({
      template: './index.html'
    }),
    new cleanWebpackPlugin(),
  ]
}

Исправлятьpackage.jsonизscript:

{
  "scripts": {
    "dev": "webpack-dev-server --config ./build/webpack.dev.js",
    "build": "webpack --config ./build/webpack.prod.js"
  },
}

Среда разработки: запуститьnpm run dev, откройте браузер для доступаhttp://localhost:8080/вы можете увидеть результат Производственная среда: запуститьnpm run build


6. Использование плагина SplitChunksPlugin

1. Знакомство с плагином SplitChunksPlugin

webpack 4УдалитьCommonsChunkPlugin,заменяетсяSplitChunksPlugin. Представьте нижеSplitChunksPluginПрименение.

существуетwebpack4目录под новымdemo6目录,Будуdemo5目录Все файлы копируются вdemo6середина.

установить Лодаш

npm i lodash --save

существуетpackage.jsonДобавить к

"scripts": {
    "dev-build": "webpack --config ./build/webpack.dev.js"
  }

Исправлятьindex.js

import _ from 'lodash'
console.log(_.join(['lodash', 'babel', 'webpack'], '-'))

бегатьnpm run dev-buildПосле этого структура каталогов demo6 выглядит следующим образом

demo6
├── demo6/build
│   ├── demo6/build/webpack.base.js
│   ├── demo6/build/webpack.dev.js
│   └── demo6/build/webpack.prod.js
├── demo6/dist
│   ├── demo6/dist/index.html
│   └── demo6/dist/main.js
├── demo6/index.html
├── demo6/package.json
├── demo6/package-lock.json
├── demo6/postcss.config.js
└── demo6/src
    └── demo6/src/index.js

В каталоге dist есть только js файл main.js, а в main.js запакована и библиотека lodash.В этом случае файл станет больше и скорость загрузки страницы будет медленнее.Нам нужно запаковать третий библиотека или должны быть упакованы отдельно.Код разделен.

В настоящее время вам нужно использовать плагин, который поставляется с webpack4.SplitChunksPlugin, по умолчанию это повлияет только на блоки кода, загружаемые по запросу.

существуетwebpack.config.jsДобавить кoptimization.splitChunks.chunks

optimization: {
   splitChunks: {
    //chunks: all, async, initial.
    //async针对异步加载的chunk做切割,initial针对初始chunk,all针对所有chunk。
     chunks: 'async'
   }
}

бегатьnpm run dev-buildПосле этого упакованный код не разбивается.

Исправлятьoptimization.splitChunks.chunksдляall

optimization: {
   splitChunks: {
    //chunks: all, async, initial.
    //async针对异步加载的chunk做切割,initial针对初始chunk,all针对所有chunk。
     chunks: 'all'
   }
}

бегатьnpm run dev-buildПосле этого структура каталогов demo6 выглядит следующим образом

demo6
├── demo6/build
│   ├── demo6/build/webpack.base.js
│   ├── demo6/build/webpack.dev.js
│   └── demo6/build/webpack.prod.js
├── demo6/dist
│   ├── demo6/dist/index.html
│   ├── demo6/dist/main.js
│   └── demo6/dist/vendors~main.js
├── demo6/index.html
├── demo6/package.json
├── demo6/package-lock.json
├── demo6/postcss.config.js
└── demo6/src
    └── demo6/src/index.js

Вы можете видеть, что в каталоге dist есть еще многоvendors~main.jsдокумент, описаниеSplitChunksPluginПлагин работает

см. далееoptimization.splitChunksконфигурация по умолчанию

module.exports = {
  //...
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }
}
  • чанки: указывает, какие чанки будут оптимизированы, необязательный асинхронный, начальный, все, асинхронный для асинхронно загружаемых модулей, начальный для начальных модулей, все для всех модулей

  • minSize: Загружаемый модуль должен быть не менее 30кб перед разбиением

  • minChunks: сгенерированоchunk, чанки, разделяющие модуль, должны быть не меньше 1 до разделения

  • maxAsyncRequests: максимальное количество параллельных запросов при загрузке по требованию.

  • maxInitialRequests: максимальное количество параллельных запросов в точке входа.

  • AutomaticNameDelimiter: по умолчанию веб-пакет генерирует имя с именем и именем чанка (например, vendors~main.js). Этот параметр указывает разделитель, используемый для сгенерированных имен.

  • Имя: имя сгенерированного куска, если установлено значение true, имя будет сгенерировано на основе комбинации конфигурации группы модуля и кэша.

  • cacheGroups: группы кеша могут наследовать и/или переопределять любые параметры splitChunks.*, однако тест, приоритет и повторное использованиеExistingChunk можно настроить только на уровне группы кеша. Чтобы отключить любую группу кеша по умолчанию, установите для нее значение false.

  • test: модуль, который управляет выбором этой группы кеша

  • приоритет: модуль может принадлежать к нескольким группам кеша, а модуль принадлежит к группе кеша с высоким приоритетом

  • reuseExistingChunk: если текущий фрагмент содержит разделенный модуль, он будет повторно использован вместо создания нового фрагмента.

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

Измените index.js для асинхронной загрузки lodash.

function getComponent(){
  return import('lodash').then(({default: _})=>{
    var element=document.createElement('div')
    element.innerHTML=_.join(['lodash', 'babel', 'webpack'], '-')
    return element
  })
}

getComponent().then(element=>{
  document.body.appendChild(element)
})

беги в это времяnpm run dev-buildСообщит об ошибке, нужно скачать и установить@babel/plugin-syntax-dynamic-import npm i @babel/plugin-syntax-dynamic-import -D

добавить в .babelrc

 "plugins": ["@babel/plugin-syntax-dynamic-import"] 

бежать сноваnpm run dev-build, на данный момент упаковка выполнена успешно, и структура каталогов выглядит следующим образом

demo6
├── demo6/build
│   ├── demo6/build/webpack.base.js
│   ├── demo6/build/webpack.dev.js
│   └── demo6/build/webpack.prod.js
├── demo6/dist
│   ├── demo6/dist/0.js
│   ├── demo6/dist/index.html
│   └── demo6/dist/main.js
├── demo6/index.html
├── demo6/package.json
├── demo6/package-lock.json
├── demo6/postcss.config.js
└── demo6/src
    └── demo6/src/index.js

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

Изменить index.js,Добавить к/* webpackChunkName:"lodash" */

function getComponent(){
  return import(/* webpackChunkName:"lodash" */'lodash').then(({default: _})=>{
    var element=document.createElement('div')
    element.innerHTML=_.join(['lodash', 'babel', 'webpack'], '-')
    return element
  })
}

getComponent().then(element=>{
  document.body.appendChild(element)
})

бегатьnpm run dev-build,Обнаружить0.jsсталиvendors~lodash.js

также можно установитьoptimization.splitChunks.cacheGroups.vendors.nameизменить имя упакованного файла

Исправлятьoptimization.splitChunksнастроить

optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 30000,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          name: 'vendors'
        },
        default: {
          minChunks: 1,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  },

бегатьnpm run dev-build, обнаружил, что имя упакованного файла становитсяvendors.js

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

Для получения подробной информации, пожалуйста, посетите официальный сайт


Семь, сегментация кода css

MiniCssExtractPluginПлагин извлекает CSS в отдельные файлы. Он создает файл CSS для каждого файла JS, который содержит CSS, доступный только в веб-пакете 4.

Создайте новый demo7 в каталоге webpack, скопируйте все файлы из demo6 в demo7 и введите demo7

Установитьnpm install --save-dev mini-css-extract-plugin

Официальный сайт предлагает использовать этот плагин в производственной среде, поэтому измените соответствующую конфигурацию webpack.

Первое местоwebpack.base.jsСкопируйте и вставьте конфигурацию загрузчика для обработки css и scss (удалите эту часть в webpack.base.js) вwebpack.prod.jsа такжеwebpack.dev.jsсередина.

существуетwebpack.prod.jsвведен вconst miniCssExtractPlugin = require('mini-css-extract-plugin')Соответствующая конфигурация после модификации выглядит следующим образом:

webpack.base.js
const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
const cleanWebpackPlugin = require('clean-webpack-plugin')

module.exports = {
  entry: {
    main: './src/index.js'  //对应filename
    //入口文件引入的模块,分割打包的名字对应chunkFilename
  },
  output: {
    filename: '[name].js', 
    chunkFilename: '[name].chunk.js',
    path: path.resolve(__dirname, '../dist')
  },
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif)$/,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name].[ext]',
            outputPath: 'images/',
            limit: 2048
          }
        }
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      }
    ]
  },

  plugins: [
    new htmlWebpackPlugin({
      template: './index.html'
    }),
    new cleanWebpackPlugin(),
  ],
  optimization: {
    usedExports: true,
    splitChunks: {
      //chunks: all, async, initial.
      //async 只对异步分割有效
      // initial同步
      //all 要配置cacheGroups
      chunks: 'all',
      // 引入的包或是库大于30kb才对代码进行分割
      minSize: 30000,

      maxSize: 0,   //没多大意义
      minChunks: 1, //当一个模块至少引入多少次时才会进行代码分割
      maxAsyncRequests: 5,  //同时加载的模块数最多是5个
      maxInitialRequests: 3,
      automaticNameDelimiter: '~', //打包后的文件名字之间的连接符
      name: true,
      cacheGroups: {    //缓存组
        // vendors: false,
        vendors: {
          //同步  检查是否在node_modules里面
          test: /[\\/]node_modules[\\/]/,
          priority: -10,    //优先级
          name: 'vendors'
        },
        // default: false
        default: {
          minChunks: 1,
          priority: -20,
          reuseExistingChunk: true, //如果模块已经打包过了就引用之前打包好的模块
          // filename: 'common.js'
        }
      }
    }
  }
}
webpack.prod.js
const merge = require('webpack-merge')
const baseConfig=require('./webpack.base')
const miniCssExtractPlugin = require('mini-css-extract-plugin')

const prodConfig={
  mode: 'production', 
  devtool: 'cheap-module-source-map',
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          miniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader'
        ]
      },
      {
        test: /\.scss$/,
        use: [
          miniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              modules: true
            }
          },
          'postcss-loader',
          'sass-loader'
        ]
      },
    ]
  },
 
  plugins: [
    new miniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[name].chunk.css'
    })
  ]
}

module.exports=merge(baseConfig,prodConfig)
webapck.dev.js
const webpack=require('webpack')
const merge = require('webpack-merge')
const baseConfig=require('./webpack.base')

const devConfig={
  mode: 'development', 
  // devtool: 'cheap-module-eval-source-map',
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader',
          'postcss-loader'
        ]
      },
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              modules: true
            }
          },
          'sass-loader',
          'postcss-loader'
        ]
      },
    ]
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ],
  
  devServer: {
    contentBase: './dist',
    // open: true, //自动打开浏览器
    // port: 8080,
    hot: true,  //启用webpack的热模块替换功能
    //hotOnly: true
    //devServer.hot在没有页面刷新的情况下启用热模块替换作为构建失败时的后备
  }
}

module.exports=merge(baseConfig,devConfig)

Следует отметить, что когда мы ранее настраивали встряхивание дерева, мы добавили элемент конфигурации sideEffects в package.json, и нам нужно изменить этот элемент конфигурации как

"sideEffects": [
    "*.css"
  ],

в противном случае пройтиimport './*.css'Файл css, введенный методом, будет удален.

бегатьnpm run build, мы видим, что в каталоге dist файлы css разделены отдельно.

Официальный сайт также предоставляет плагин для сжатия файлов css.

Установитьnpm i optimize-css-assets-webpack-plugin -D

Изменить webpack.prod.js
//引入
const optimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
//添加
optimization: {
    minimizer: [new optimizeCSSAssetsPlugin({})],
  },

бежать сноваnpm run build, откройте упакованный файл css и обнаружите, что файл css сжат.

Для более конкретного использования, пожалуйста, посетите официальный сайт


Код загружен на гитхаб:github:webpack4