Система длинных текстов 4D разбирает основы Webpack (Часть 2)

Webpack
Система длинных текстов 4D разбирает основы Webpack (Часть 2)

плагин веб-пакета

процесс сборки

  webpack loaderотвечает за перевод разных типов файлов, преобразование их вwebpackмодули, которые можно получить. а такжеwebpackплагины сloaderСуществует большая разница,webpackПлагины проходят через весь процесс сборки. На каждом этапе процесса сборки будут запускаться разные функции ловушек. Выполнение некоторой обработки в разных функциях ловушекwebpackЧто делает плагин.

  webpackПолный процесс сборки пакета выглядит следующим образом.

  • Параметры инициализации:cliаргументы командной строки сwebpackСлияние профиля, полученное путем распределения объекта параметра
  • Загрузка плагина: объект параметра передается вwebpackИнициализировать генерациюcompilerОбъект, выполните вставку операторов инстанцирования в файл конфигурации (например,new HtmlWebpackPlugin()),дляwebpackПоток событий зависает на пользовательскомhooks
  • Начать компиляцию: выполнитьcompilerобъектrunметод начинает компилироваться каждый разrunКомпиляция создастcompilationобъект
  • Определить вход: триггерcompilerобъектmakeспособ начать анализ входного файла
  • Модуль компиляции: начиная с входного файла, вызовитеloaderПереведите модуль, затем найдите модули, от которых зависит модуль, и переведите, и рекурсивно завершите перевод всех модулей.
  • Завершение компиляции: Соберите по одному в соответствии с зависимостями между записью и модулемchunk,воплощать в жизньcompilationизsealметод для каждогоchunkОрганизовать, оптимизировать, упаковать
  • Выходные ресурсы: исполнениеcompilerизemitAssetsспособ вывода сгенерированного файла вoutputв каталоге

在这里插入图片描述

пользовательский плагин

  webpackВозможности плагина следующие.

  • независимыйjsмодуль, который предоставляет соответствующие функции
  • на прототипе функцииapplyметод будет вводитьcompilerобъект
  • compilerСоответствующий объект монтируется наwebpackкрюк
  • В функции обратного вызова обработчика событий вы можете получить скомпилированныйcompilationОбъект, если это асинхронный хук, вы также можете получить соответствующийcallbackфункция
class CustomDlugins {
  constructor() {}
  apply(compiler) {
    compiler.hooks.emit.tapAsync(
      "CustomDlugins",
      (compilation, callback) => {}
    )
  }
}

module.exports = CustomDlugins

   Большинство плагинов, ориентированных на пользователя, идут первыми.compilerзарегистрироваться следующим образомcompilerНекоторые часто используемые хуки, выставленные на .

крюк Типы эффект
run AsyncSeriesHook Выполняется до того, как компилятор начнет чтение записей
compiler SyncHook в новомcompilationВыполнить перед созданием
compilation SyncHook Еще один разcompilationЗапустить плагин после создания
make AsyncSeriesHook Выполнить перед завершением компиляции
emit AsyncSeriesHook генерируетсяoutputВыполняется перед каталогом, параметры обратного вызоваcompilation
afterEmit AsyncSeriesHook в make-файле дляoutputВыполнить после каталога
assetEmitted AsyncSeriesHook Выполняется при создании файла, предоставляя доступ к информации выходного файла, параметрам обратного вызоваfile,info
done AsyncSeriesHook Выполнить после завершения компиляции, параметры обратного вызоваstats

   Плагин пользовательского списка файлов, автоматически генерирующий список файлов после упаковки, записывающий список файлов и количество файлов.

Включая корневой каталогpackage.json,webpack.config.jsа такжеsrc,srcвключить нижеmain.js.

// package.json
{
  ...
  "scripts": {
    "build": "webpack"
  },
  "devDependencies": {
    "webpack": "4.29.4",
    "webpack-cli": "3.2.3"
  }
}

// webpack.config.js
module.exports = {
  entry: "./src/main.js",
  output: {
    filename: "./[name].js"
  },
  plugins: []
}

// src/main.js
console.log("hello world")

   Затем продолжайте создавать в корневом каталогеpluginsпапка, где новыеFileListPlugin.jsдокумент,webpack.config.jsВнедрить плагины.

   Обратите внимание, что эта сцена должна быть сгенерирована в файле дляdistкаталог раньше, так что для регистрации естьcompilerВверхemitкрюк.emitЭто асинхронный последовательный хук, сtapAsyncзарегистрироваться.

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

   Иди наконецcompilation.assetsДобавьте новые файлы.

// plugins/FileListPlugin.js
class FileListPlugin {
  constructor(options) {
    this.filename =
      options && options.filename ? options.filename : "FILELIST.md"
  }

  apply(compiler) {
    compiler.hooks.emit.tapAsync("FileListPlugin", (compilation, callback) => {
      const keys = Object.keys(compilation.assets)
      const length = keys.length

      var content = `# ${length} file${
        length > 1 ? "s" : ""
      } emitted by webpack\n\n`

      keys.forEach((key) => {
        content += `- ${key}\n`
      })

      compilation.assets[this.filename] = {
        source: function () {
          return content
        },
        size: function () {
          return content.length
        }
      }

      callback()
    })
  }
}

module.exports = FileListPlugin

// webpack.config.js
const FileListPlugin = require("./plugins/FileListPlugin")

module.exports = {
  ...
  plugins: [
    new FileListPlugin({
      filename: "filelist.md"
    })
  ]
}

оптимизация разработки

плагин веб-пакета

webpack-dashboard

  webpack-dashboardиспользуется для оптимизацииwebpackинструмент журнала.

  Корневой каталогwebpack.config.js,package.jsonа такжеsrc,srcвключить нижеmain.js.

// package.json
{
  ...
  "scripts": {
    "build": "webpack"
  },
  "devDependencies": {
    "vue": "^2.6.12",
    "webpack": "4.29.4",
    "webpack-cli": "3.2.3",
    "webpack-dashboard": "^2.0.0"
  }
}

// webpack.config.js
const DashboardPlugin = require("webpack-dashboard/plugin")

module.exports = {
  entry: "./src/main.js",
  output: {
    filename: "./[name].js",
  },
  plugins: [
      new DashboardPlugin()
  ],
  mode: "development"
}

// src/main.js
import vue from "vue"

console.log(vue)

Использоватьwebpack-dashboardВступите в силу, но также измените исходную команду запуска.

// package.json
{
    ...
    "scripts": {
        "build": "webpack-dashboard -- webpack"
    }
}

бегатьbuildПосле команды консоль напечатает следующее содержимое, левый верхний уголLogдляwebpackсобственный журнал, внизу слеваModulesНа этот раз в упаковке участвовал именно модуль, можно посмотреть занимаемый объем и пропорции модуля, правый нижний уголProblemsВы можете просматривать предупреждения и ошибки процесса сборки и т. д.

在这里插入图片描述

speed-measure-webpack-plugin

  speed-measure-webpack-plugin(SMP) можно анализироватьwebpackНа протяжении всего процесса упаковки в каждомloaderа такжеpluginПо результатам анализа можно узнать, какие этапы сборки занимают больше времени для оптимизации и итеративного тестирования.

  SMPПри его использовании необходимоwrapметод завернут вwebpackвне объекта конфигурации.

// webpack.config.js
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin")
const smp = new SpeedMeasurePlugin()

module.exports = smp.wrap({
  entry: "./src/main.js",
  output: {
    filename: "./[name].js"
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: "babel-loader",
        exclude: /node_modules/,
        options: {
          cacheDirectory: true,
          presets: [["@babel/preset-env", { modules: false }]]
        }
      }
    ]
  }
})

// src/main.js
const fn = () => {
  console.log("hello world")
}

fn()

// package.json
{
  ...
  "scripts": {
    "build": "webpack"
  },
  "devDependencies": {
    "webpack": "4.29.4",
    "webpack-cli": "3.2.3",
    "@babel/core": "^7.2.2",
    "@babel/preset-env": "^7.3.1",
    "babel-loader": "^8.0.5",
    "speed-measure-webpack-plugin": "^1.2.2"
  }
}

бегатьbuildПосле того, как скрипт упакован, вывод консоли выглядит следующим образом, как видноbabel-loaderпотрачено на перевод1.16Второй.

在这里插入图片描述

webpack-merge

  webpack-mergeДля проектов, которым необходимо настроить несколько сред упаковки.

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

  Корневой каталогpackage.json,srcа такжеbuild,srcвключить нижеindex.html,main.js,buildвключить нижеwebpack.base.conf.js,webpack.dev.conf.jsа такжеwebpack.prod.conf.js.

// package.json
{
  ...
  "scripts": {
    "dev": "webpack-dev-server --config=./build/webpack.dev.conf.js",
    "build": "webpack --config=./build/webpack.prod.conf.js"
  },
  "devDependencies": {
    "webpack": "4.29.4",
    "webpack-cli": "3.2.3",
    "webpack-dev-server": "3.1.14",
    "webpack-merge": "^4.1.4",
    "file-loader": "^1.1.6",
    "css-loader": "^0.28.7",
    "style-loader": "^0.19.0",
    "html-webpack-plugin": "3.2.0"
  }
}

// src/main.js
console.log("hello world")

// src/index.html
<html lang="zh-CN">
  <body>
    <p>hello world</p>
  </body>
</html>

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

// build/webpack.base.conf.js
const HtmlWebpackPlugin = require("html-webpack-plugin")

module.exports = {
  entry: "./src/main.js",
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif)$/,
        use: "file-loader",
      },
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./src/index.html",
    }),
  ],
}

Конфигурация среды разработки    следующая, среди которыхwebpack-mergeв слиянииmodule.rulesВ процессе будетtestАтрибуты используются в качестве идентификаторов.При обнаружении одного и того же элемента предыдущее правило будет перезаписано последним правилом, поэтому не нужно добавлять избыточный код.

   Следующая среда разработкиloaderвключатьfile-loader,css-loader,babel-loadercss-loaderа такжеbabel-loaderпокрыты доloaderи включилsourceMap.

// build/webpack.dev.conf.js
const baseConfig = require("./webpack.base.conf.js")
const merge = require("webpack-merge")

module.exports = merge.smart(baseConfig, {
  output: {
    filename: "./[name].js",
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: {
              sourceMap: true,
            },
          },
        ],
      },
    ],
  },
  devServer: {
    port: 3000,
  },
  mode: "development",
})

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

// build/webpack.prod.conf.js
const baseConfig = require("./webpack.base.conf.js")
const merge = require("webpack-merge")

module.exports = merge.smart(baseConfig, {
  output: {
    filename: "./[name].[chunkhash:8].js",
  },
  mode: "production",
})

Горячая замена модуля

Автообновление(live reload), то есть пока код будет меняться, он будет пересобираться, а потом страница будет обновляться. а такжеwebpackСделав еще один шаг вперед, вы можете получить последние изменения кода без обновления веб-страницы, то есть горячую замену модуля (Hot Module Replacement,HMR).

настроить

  HMRДля включения требуется ручная настройка, следующая конфигурация будет привязана к каждому модулю.module.hotобъект, содержащийHMRизAPI(например, можно включить или выключить для определенных модулейHMRЖдать).

// webpack.config.js
const webpack = require("webpack")

module.exports = {
  ...
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ],
  devServer: {
    hot: true
  }
}

   необходимо вызывать вручную после настройкиmodule.hotВверхAPIвключатьHMR. следующим образом, еслиmain.jsявляется точкой входа приложения, вы можете позвонитьHMR APIкод в этой записи, тоmain.jsи все модули, от которых он зависит, будут включеныHMR. Когда модуль будет изменен,HMRвызовет повторное выполнение приложения в текущей средеmain.js, но сама страница не обновляется.

// main.js
...

if (module.hot){
    module.hot.accept()
}

   Если логика приложения сложная, не рекомендуется использоватьwebpackизHMR,потому чтоHMRВ процессе запуска могут возникнуть непредвиденные проблемы. Разработчикам рекомендуется использовать сторонние предоставленныеHMRрешения, такие какvue-loader,react-hot-loader.

Включить HMR

  Корневой каталогwebpack.config.js,package.jsonа такжеsrc,srcвключить нижеmain.js,index.htmlа такжеutils.js.

// webpack.config.js
const webpack = require("webpack")

module.exports = {
  entry: "./src/main.js",
  output: {
    filename: "./[name].js",
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new HtmlWebpackPlugin({
      template: "./src/index.html"
    })
  ],
  devServer: {
    hot: true
  }
}

// package.json
{
  ...
  "scripts": {
    "dev": "webpack-dev-server"
  },
  "devDependencies": {
    "webpack": "4.29.4",
    "webpack-cli": "3.2.3",
    "webpack-dev-server": "3.1.14",
    "html-webpack-plugin": "3.2.0"
  }
}

// src/main.js
import { logToHtml } from "./utils.js"

var count = 0

setInterval(() => {
  count += 1
  logToHtml(count)
}, 1000)

// src/utils.js
export function logToHtml(count) {
  document.body.innerHTML = `count: ${count}`
}

// src/index.html
<html lang="zh-CN">
  <body></body>
</html>

бегатьdevПосле команды script вывод консоли выглядит следующим образом, нажмитеhttp://localhost:8080/Открытьhtml.

在这里插入图片描述

  htmlвыводить целое число и добавлять каждую секунду1,Исправлятьutils.jsКак следует, сохраните и просмотритеhtml, обновление страницы, засчитано ранееcountперезапустить через0добавить в секунду1(Частично не обновлен).

// src/utils.js
export function logToHtml(count) {
  document.body.innerHTML = `count update: ${count}`
}

  utils.jsснижение,main.jsДобавьте следующий код, чтобы включитьHMR.

// src/main.js
...

if (module.hot) {
  module.hot.accept()
}

   и снова измените егоutils.js,ПроверитьhtmlНе обновленный, а частично обновленный,countЗначение также добавляется на основе предыдущего1.

   Но это принесет другую проблему, нынешнююhtmlуже есть одинsetInterval,а такжеHMRдобавит новыеsetInterval, не очистил предыдущий, что привело к окончательномуhtmlТам мигают разные цифры.

Чтобы избежать этой проблемы, когдаmain.jsЕсли есть изменение, обновите всю страницу, чтобы предотвратить несколько таймеров, но продолжайте открывать другие модули.HMR.

// src/main.js
...

if (module.hot) {
  module.hot.decline()
  module.hot.accept(["./utils.js"])
}

процесс ГМР

  Первый запуск проектаdevСкрипт сначала будет собран и запакован, а заодно в инжектится код как обновить модуль и нужно ли обновлять модуль после полученияbundleсередина.

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

在这里插入图片描述

с последующимwebpack-dev-serverиспользоватьexpressЗапустите локальную службу, чтобы браузер мог запрашивать локальные ресурсы. затем перезапуститеwebsocketСервис для установления двусторонней связи между браузером и локальным сервисом.

  Нажмитеhttp://localhost:8081/Откройте страницу в браузере, в это время страница устанавливает соединение с локальным сервисомwebsocketПодключитесь, и локальный сервисhashвозвращаемое значение.

在这里插入图片描述

   страница, чтобы получитьhashпосле этогоhashкак следующий сервер запросовjsа такжеjsonизhash.

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

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

在这里插入图片描述

   После завершения компиляции локальный сервис пропускается черезwebsocketотправить этот пакетhashна страницу.

在这里插入图片描述

   страница, чтобы получитьhashПосле, построить[hash].hot-update.jsonа также[hash].hot-update.js, за которым следуетajaxзапросить, получитьjsonфайл, этоjsonФайл включает в себя все модули, подлежащие обновлению. затем пройти сноваjsonpЗапрос на получение последнего кода модуля.

вjsonВ содержимом возврата файлаhУказывает вновь созданныйhashзначение, префикс, используемый для ресурса запроса горячей замены следующего файла,cУказывает, что текущий файл для горячей замены соответствуетmainмодуль.

在这里插入图片描述

  jsВозвращаемое содержимое файла является кодом этой модификации.

在这里插入图片描述

После того, как страница    получит данные запроса, она сравнит старый и новый модули, чтобы решить, следует ли обновлять модуль. Обратите внимание, что если во время горячего обновления произойдет ошибка, горячее обновление вернется кlive reload, то есть выполните обновление браузера, чтобы получить последний упакованный код.

упаковочный инструмент

RollUp

  RollUpСлишкомJavaScriptМодуль Packer, который больше ориентирован наJavaScriptупаковка, не такая универсальная, какwebpack. Но по сравнению с другими упаковочными инструментами,RollUpВсегда упаковывайте меньший и более быстрый пакет.RollUpдля кодаtree shakingа такжеes6Модули поддерживаются алгоритмическими преимуществами. Поэтому он обычно используется для разработки приложений.webpack, используемый при разработке библиотекиRollUp.

а такжеwebpackКак правило, внутренняя установка проекта отличается.RollUpМожет быть установлен непосредственно глобально.

npm i rollup -g

  Корневой каталог включаетpackage.json,rollup.config.jsа такжеsrc,srcНижеmain.js. вrollup.config.jsсерединаoutput.formatявляется модульной формой выходного ресурса, это свойствоwebpackнедоступно. используется следующим образомcjs(CommonJs),Кроме тогоamd,es(ES Module),umd,iife(самостоятельная функция),system(SystemJsформат загрузчика).

// package.json
{
  ...
  "scripts": {
    "build": "rollup -c rollup.config.js"
  }
}

// rollup.config.js
module.exports = {
    input: "src/main.js",
    output: {
        file: "dist/bundle.js",
        format: "cjs"
    }
}

// src/main.js
console.log("hello world")

бегатьbuildскрипт, корневой каталогdistвыход внизbundle.js. Вы можете четко видеть упакованныйbundleОчень чистый,RollUpНикакого дополнительного кода не добавлялось, и тот же исходный код,webpackУпаковка добавляет много дополнительного кода.

// dist/bundle.js
"use strict"

console.log("hello world")

такжеtree shakingСамая характеристика - это началоRollUpреализуется, на основеES6 Moduleстатический анализ, чтобы выяснить, на какие модули нет ссылок, и, наконец, удалить их из сгенерированногоbundleне входит.

Parcel

  ParcelсуществуетJavaScriptИнструмент упаковки относительно поздно появился, в тесте на его официальном сайте скорость его сборки сравнивается сwebpackЭто в несколько раз быстрее и работает из коробки с нулевой конфигурацией.

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

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

  webpackпройти черезloaderработать с различными видами ресурсов,loaderСуть — это функция, вход и выход которой — строки. Напримерbabel-loader,войтиES6+, вывод после преобразования синтаксисаES5. Общий процесс заключается вES6Содержимое строки анализируется какAST(Абстрактное синтаксическое дерево), даASTПреобразование синтаксиса, генерацияES5код и вернуть строку.

  Если вbabel-loaderзатем добавьте большеloader, и его процесс обработки выглядит следующим образом. который включает в себя большое количествоStringа такжеASTконверсия,loaderОни не влияют друг на друга и выполняют свои обязанности.Хотя может быть некоторая избыточность, это способствует поддержаниюloaderнезависимость и ремонтопригодность.

          资源输入
             ↓
loader1   (String -> AST) --> 语法转换 --> (AST -> String)
                                                 ↓
loader2   (AST -> String) <-- 语法转换 <-- (String -> AST)
                 ↓
loader3   (String -> AST) --> 语法转换 --> (AST -> String)
                                                 ↓
                                              资源输出

а такжеParcelНе открытоloaderконцепция, его поток обработки ресурсов не похож наwebpackдаloaderПроизвольное сочетание, поэтому не требует многоASTа такжеStringпреобразование между.

   следующим образом Для каждого шагаAST, то следующим шагом будет прямое использование проанализированных и преобразованных на предыдущем шагеASTВот и все, просто используйте его на последнем шагеASTвернутьсяString. Для огромного проекта парсингASTОчень много времени, оптимизация здесь сэкономит много времени.

           资源输入
              ↓
process1   (String -> AST) --> 语法转换
                                  ↓ (process1 返回的 AST)
process2                       语法转换
                                  ↓ (process2 返回的 AST)
process3                       语法转换 --> (AST -> String)
                                                  ↓
                                               资源输出

  ParcelЕго также можно установить непосредственно глобально.

npm i -g parcel-bundler

  Корневой каталог включаетpackage.jsonа такжеsrc,srcНижеindex.jsа такжеindex.html. вParcelдоступенhtmlфайл как запись проекта, изhtmlНачните дальше искать зависимые ресурсы.

  ParcelОн не принадлежал к своим собственным профилям, но, по сути, конфигурация раскола, чтобыbabel,PostCssи другие специальные инструменты, которыми нужно управлять отдельно. Например.babelrc,ParcelОн будет использоваться при упаковке какES6Разборный код конфигурации.

// package.json
{
  ...
  "scripts": {
    "dev": "parcel ./src/index.html",
    "build": "parcel build ./src/index.html"
  }
}

// src/index.html
<html lang="zh-CN">
  <body>
    <p>hello world</p>
    <script src="./index.js"></script>
  </body>
</html>

// src/index.js
console.log("hello world")

Предыдущий

🎉 Напишите в конце

🍻Ребята, если вы это видели и считаете, что эта статья была вам полезна, ставьте лайк 👍 илиStar✨Поддержите!

Кодирование вручную, если есть ошибки, исправьте их в комментариях 💬~

Ваша поддержка — самая большая мотивация для меня обновляться💪~

GitHub,Blog,Наггетс,CSDNСинхронизированное обновление, подписывайтесь 😉~