плагин веб-пакета
процесс сборки
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-loader
,вcss-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Синхронизированное обновление, подписывайтесь 😉~