Благодаря своим мощным функциям веб-пакет стал самым популярным и активным инструментом упаковки, а также «мягким навыком», которым старшие программисты должны овладеть во время собеседований; автор объединяет опыт проекта, чтобы представить использование веб-пакета; эта статья вводная статья, основная Введение в ввод и вывод webpack, использование различных загрузчиков и плагинов, а также построение среды разработки Для технических специалистов, пожалуйста ctrl+w.
Все демо-коды в этой статье находятся вWebpackDemo
концепция
Давайте посмотрим на определение webpack на официальном сайте:
По сути, webpack — это сборщик статических модулей для современных приложений JavaScript. Когда webpack обрабатывает приложение, он рекурсивно строит граф зависимостей, содержащий каждый модуль, необходимый приложению, а затем объединяет все эти модули в один или несколько пакетов.
Во-первых, webpack — это упаковщик статических модулей, к так называемым статическим модулям относятся скрипты, таблицы стилей, картинки и т. д. При упаковке webpack сначала проходит все статические ресурсы, строит граф зависимостей по ссылкам на ресурсы. , а затем объединяет модули Разделить и упаковать один или несколько пакетов. Давайте еще раз взглянем на картинку на официальном сайте, которая наглядно описывает процесс:
Когда вы упоминаете WebPack, вы должны упомянуть четыре Webpack.Основная идея
- Запись: указывает, какой модуль веб-пакет должен использовать в качестве начала построения своего внутреннего графа зависимостей.
- output: куда выводить созданные им пакеты
- загрузчик: позволяет веб-пакету обрабатывать файлы, отличные от JavaScript
- Плагины: используются для выполнения более широкого круга задач
твой первый упаковщик
Сначала устанавливаем вебпак глобально:
npm install webpack webpack-cli –g
webpack можно собрать напрямую через командную строку без использования конфигурационного файла.Использование выглядит следующим образом:
webpack <entry> [<entry>] -o <output>
Запись и вывод здесь соответствуют записи и вводу в вышеприведенных понятиях Давайте создадим новый файл ввода:
//demo1/index.js
var a = 1
console.log(a)
document.write('hello webpack')
С входным файлом нам также необходимо определить входной путь dist/bundle.js через командную строку:
webpack index.js -o dist/bundle.js
Таким образом, webpack создаст упакованные файлы в каталоге dist.
Мы также можем создать новый html-файл в каталоге проекта, чтобы импортировать упакованный файл bundle.js для просмотра эффекта.
конфигурационный файл
Метод упаковки командной строки ограничен простыми проектами.Если наш проект сложный и имеет несколько записей, мы не можем записывать записи каждый раз, когда мы упаковываем, поэтому в общих проектах для упаковки используются конфигурационные файлы; Метод команды следующий:
webpack [--config webpack.config.js]
Имя файла конфигурации по умолчанию:webpack.config.js
, в проекте часто бывает несколько наборов файлов конфигурации, мы можем настраивать разные файлы для разных сред с помощью--config
переключить:
//生产环境配置
webpack --config webpack.prod.config.js
//开发环境配置
webpack --config webpack.dev.config.js
Несколько типов конфигурации
конфигурационный файл черезmodule.exports
Экспорт объекта конфигурации:
//webpack.config.js
var path = require('path');
module.exports = {
mode: 'development',
//入口文件
entry: './index.js',
//输出目录
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
}
};
Помимо экспорта как объекта, его также можно экспортировать как функцию.Функция будет вносить параметры, такие как переменные среды, переданные в командной строке, чтобы переменные среды можно было настроить более удобно, например, мы находимся в формальная среда и линия онлайн-упаковки. Среда разработки может бытьenv
Различать:
var path = require('path');
//env:环境对象
module.exports = function(env, argv){
return {
//其他配置
entry: './index.js',
output: {}
}
};
также можно экспортировать как промис для асинхронной загрузки конфигурации, например, файл записи может загружаться динамически:
module.exports = () => {
return new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve({
entry: './index.js',
output: {}
})
}, 5000)
})
}
Вход
Как упоминалось выше, запись является отправной точкой всей зависимости; наша часто используемая конфигурация с одной записью — это запись страницы:
module.exports = {
entry: './index.js',
}
Это сокращение от:
module.exports = {
entry: {
main: './index.js'
},
}
Но у нас может быть более одного модуля для страницы, поэтому нам нужно внедрить несколько файлов зависимостей вместе. На данный момент нам нужно использовать массив. Код находится в demo2:
module.exports = {
entry: [
//轮播图模块
'./src/banner.js',
//主模块
'./src/index.js',
//底部模块
'./src/foot.js'
],
}
Иногда у нас есть проект, может иметь более одной страницы, вам нужно упаковать несколько страниц, а вход поддерживает форму входящих объектов, код находится в DEMO3:
//demo3
module.exports = {
entry: {
home: './src/home.js',
list: './src/list.js',
detail: ['./src/detail.js', './src/common.js'],
},
}
Таким образом, webpack создаст три разные зависимости.
выход
output
Параметры используются для управления тем, как webpack импортирует скомпилированные файловые модули; хотя записей может быть несколько, настроить можно только одну.output
:
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
//CDN地址
publicPath: '/',
},
}
Здесь настраиваем однократную запись, а на выходе bundle.js, но если стоит режим множественной записи, то не получится, webpack подскажетConflict: Multiple chunks emit assets to the same filename
, то есть несколько файловых ресурсов имеют одно и то же имя файла; webpack предоставляет占位符
Чтобы гарантировать, что каждый выходной файл имеет уникальное имя:
module.exports = {
entry: {
home: './src/home.js',
list: './src/list.js',
detail: ['./src/detail.js', './src/common.js'],
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js',
},
}
Таким образом, файлы, упакованные webpack, будут упакованы в соответствии с именем входного файла для создания трех разных файлов пакета; также есть следующие разные строки-заполнители:
Заполнитель | описывать |
---|---|
[hash] | хэш идентификатора модуля |
[chunkhash] | хэш содержимого чанка |
[name] | имя модуля |
[id] | идентификатор модуля |
[query] | Запрос модуля, например, строка после имени файла? |
Здесь вводятся понятия Module, Chunk и Bundle.Эти два существительных часто встречаются в приведенном выше коде, так в чем же между ними разница? Прежде всего, мы обнаружили, что в нашем коде часто появляются модули, такие как module.exports, при этом Chunk часто появляется с записью, а Bundle всегда появляется с выводом.
- модуль: исходный код, который мы пишем, будь то commonjs или amdjs, можно понимать как модули один за другим
- Чанк: Когда исходный файл модуля, который мы написали, отправляется в webpack для упаковки, webpack генерирует файлы фрагментов в соответствии с отношением ссылки на файл, и веб-пакет выполняет некоторые операции с этими файлами фрагментов.
- Пакет: После того, как webpack обработает файл фрагмента, он, наконец, выведет файл пакета, который содержит окончательные исходные файлы, которые были загружены и скомпилированы, поэтому его можно запустить непосредственно в браузере.
Давайте лучше разберемся в этих трех концепциях с помощью следующей картинки:
Суммировать:
модуль, чанк и бандл на самом деле являются тремя именами одного и того же логического кода в разных сценариях конвертации: то, что мы пишем напрямую, называется модулем, когда его обрабатывает вебпак, это и есть чанк, и, наконец, генерируется бандл, который может быть запущен непосредственно браузером.
хэш, чанкхэш, контентхэш
Поймите концепцию чанка, я считаю, что разницу между хэшем и хэшем в приведенной выше таблице также легко понять;
- Хэш: это связано с построением всего проекта. Пока в проекте есть изменения файлов, значение хеш-функции всего построения проекта будет меняться, и все файлы имеют одно и то же значение хеш-функции.
- Chunkhash: Это связано с созданием входного файла.Соответствующий фрагмент строится в соответствии с входным файлом, и генерируется хэш, соответствующий каждому фрагменту; если входной файл изменен, значение хеш-функции соответствующего фрагмента будет измененный.
- contenthash: это связано с содержимым самого файла, и уникальный хэш создается в соответствии с содержимым файла, то есть, когда содержимое файла изменяется, хеш изменяется.
модель
В webpack2 и webpack3 нам нужно вручную добавлять плагины для сжатия кода, определять переменные среды и обращать внимание на оценку среды, что очень громоздко; в webpack4 конфигурация режима предоставляется напрямую, что может быть используется из коробки, при игнорировании конфига webpack также выдаст предупреждение.
module.exports = {
mode: 'development',
};
//相当于
module.exports = {
devtool:'eval',
plugins: [
new webpack.NamedModulesPlugin(),
new webpack.NamedChunksPlugin(),
new webpack.DefinePlugin({
"process.env.NODE_ENV": JSON.stringify("development")
})
]
}
Режим разработки должен сообщить webpack, что я в настоящее время нахожусь в состоянии разработки, то есть упакованный контент должен быть дружественным к разработке, что удобно для отладки кода и обновлений браузера в реальном времени.
module.exports = {
mode: 'production',
};
//相当于
module.exports = {
plugins: [
new UglifyJsPlugin(/*...*/),
new webpack.DefinePlugin({
"process.env.NODE_ENV": JSON.stringify("production")
}),
new webpack.optimize.ModuleConcatenationPlugin(),
new webpack.NoEmitOnErrorsPlugin()
]
}
Производственный режим не обязательно должен быть удобным для разработки, он должен сосредоточиться только на производительности упаковки и создании пакетов меньшего размера. Увидев, что здесь используется много плагинов, не паникуйте, мы объясним их функции один за другим ниже.
Полагаю, у многих ботинок детей возникали вопросы, почему здесь используется DefinePlugin при определении переменных окруженияJSON.stringify("production")
, использовать напрямую"production"
Разве это не проще?
Сначала посмотримJSON.stringify("production")
что генерируется; результат выполнения""production""
, тут обратите внимание, дело не в том, что у вас замылены глаза или на экране маленькие черные точки, результат действительно лучше, чем"production"
Вложен дополнительный слой кавычек.
Мы можем просто понимать плагин DefinePlugin какprocess.env.NODE_ENV
заменить на строку内容
. Предположим, у нас есть следующий код для оценки окружения в коде:
//webpack.config.js
module.exports = {
plugins: [
new webpack.DefinePlugin({
"process.env.NODE_ENV": "production"
}),
]
}
//src/index.js
if (process.env.NODE_ENV === 'production') {
console.log('production');
}
Сгенерированный код будет скомпилирован следующим образом:
//dist/bundle.js
//代码中并没有定义production变量
if (production === 'production') {
console.log('production');
}
но мы не можем определить это в нашем кодеproduction
переменная, поэтому код будет напрямую сообщать об ошибке, поэтому нам нужно обернуть слой через JSON.stringify:
//webpack.config.js
module.exports = {
plugins: [
new webpack.DefinePlugin({
//"process.env.NODE_ENV": JSON.stringify("production")
//相当于
"process.env.NODE_ENV": '"production"'
}),
]
}
//dist/bundle.js
if ("production" === 'production') {
console.log('production');
}
С скомпилированным таким образом кодом проблем не возникает.
Автоматически генерировать страницы
В приведенном выше коде мы обнаружили, что index.html генерируется вручную, а затем вводится упакованный файл бандла, но это слишком громоздко, и если сгенерированный файл бандла вводит хеш-значение, сгенерированное имя файла каждый раз разное. Итак, нам нужен плагин, автоматически генерирующий html, для начала нам нужно установить этот плагин:
npm install --save-dev html-webpack-plugin
В demo3 мы создали три разных файла bundle.js, мы надеемся представить эти три файла на трех разных страницах, изменив файл конфигурации следующим образом:
module.exports = {
//省略其他代码
plugins: [
new HtmlWebpackPlugin({
//引用的模板文件
template: './index.html',
//生成的html名称
filename: 'home.html',
chunks: ['home']
}),
new HtmlWebpackPlugin({
template: './index.html',
filename: 'list.html',
chunks: ['list']
}),
new HtmlWebpackPlugin({
template: './index.html',
filename: 'detail.html',
chunks: ['detail']
}),
]
}
Мы используем index.html в качестве файла шаблона для создания трех разных страниц домашней страницы, списка и подробностей, а также вводим различные пакеты через фрагменты; если фрагменты здесь не указаны, каждая страница будет импортировать все сгенерированные комплекты.
html-webpack-plugin также поддерживает следующие поля:
new HtmlWebpackPlugin({
template: './index.html',
filename: 'all.html',
//页面注入title
title: 'html webpack plugin title',
//默认引入所有的chunks链接
chunks: 'all',
//注入页面位置
inject: true,
//启用hash
hash: true,
favicon: '',
//插入meta标签
meta: {
'viewport': 'width=device-width, initial-scale=1.0'
},
minify: {
//清除script标签引号
removeAttributeQuotes: true,
//清除html中的注释
removeComments: true,
//清除html中的空格、换行符
//将html压缩成一行
collapseWhitespace: false,
//压缩html的行内样式成一行
minifyCSS: true,
//清除内容为空的元素(慎用)
removeEmptyElements: false,
//清除style和link标签的type属性
removeStyleLinkTypeAttributes: false
}
}),
После установки заголовка выше вам необходимо установить строку шаблона в файле шаблона:
<title><%= htmlWebpackPlugin.options.title %></title>
loader
Загрузчик используется для преобразования исходного кода модуля модуля.По умолчанию веб-пакет может распознавать только код commonjs, но мы будем внедрять в код такие файлы, как vue, ts, less и т. д., которые веб-пакет не может обработать, загрузчик расширяется webpack для обработки различных файлов. Возможность печатать, конвертировать эти файлы в js, css, которые может отображать браузер.
module.rules
Позволяя нам настроить несколько загрузчиков, мы можем четко видеть, какие загрузчики применяются к текущему типу файла, а код загрузчиков находится в demo4.
{
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {}
}
},
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader' }
]
},
]
}
}
Мы видим, что значение атрибута правил представляет собой массив, каждый объект массива представляет собой другое правило совпадения; регулярное выражение, когда свойство теста отличается, сопоставление различных файловых суффиксов; использование использования означает, что называется, когда файл называется? Существует несколько погрузчик, использует использование в массивах.
Поддержка нескольких загрузчиковцепной проход, с возможностью конвейерной обработки ресурсов, возвращаемое значение, обработанное предыдущим загрузчиком, передается следующему загрузчику, обработка загрузчиком имеет приоритет,справа налево, снизу вверх; В приведенной выше демке обработка css следует этому приоритету, сначала его обработает css-загрузчик, а потом отдаст style-загрузчику, поэтому следует также обратить внимание на порядок до и после написания загрузчика.
css-загрузчик и загрузчик стилей
CSS-Loader и Style-Loader похожи по названиям, но возможности обоих имеют большую разницу, но они часто их используют; способы установки:
npm i -D css-loader style-loader
css-loader используется для объяснения @import и url(); style-loader используется для передачи таблицы стилей, сгенерированной css-loader, через<style>标签
, вставьте его на страницу.
/* /src/head.css */
.head{
display: flex;
background: #666;
}
/* /src/foot.css */
.foot{
background: #ccc;
}
/* /src/index.css */
@import './head.css';
@import './foot.css';
.wrap {
background: #999;
}
Затем импортируйте index.css в файл ввода, и вы увидите эффект упаковки.На страницу вставляются три тега стиля, а код находится в demo4:
sass-загрузчик и меньше-загрузчик
Эти два загрузчика можно догадаться из названия, они используются для работы с sass и less стилями. способ установки:
npm i -D sass-loader less-loader node-sass
Настроить в конфиге, код есть в demo4:
{
//其他配置
rules: {
test: /\.scss$/,
use: [{
loader: 'style-loader'
}, {
loader: 'css-loader'
},{
loader: 'sass-loader'
}]
},{
test: /\.less$/,
use: [{
loader: 'style-loader'
}, {
loader: 'css-loader'
},{
loader: 'less-loader'
}]
}
}
postcss-loader
На дворе 0202 год, и мои друзья точно не хотят вручную добавлять -moz, -ms, -webkit и другие приватные префиксы браузера один за другим; postcss предоставляет множество функций расширения для стилей; ничего не говори, установи его первый:
npm i -D postcss-loader
Старое правило, либо настроить его в конфиге:
rules: [{
test: /\.scss$/,
use: [{
loader: 'style-loader'
}, {
loader: 'css-loader'
}, {
loader: 'postcss-loader'
},{
loader: 'sass-loader'
}]
},{
test: /\.less$/,
use: [{
loader: 'style-loader'
}, {
loader: 'css-loader'
}, {
loader: 'postcss-loader'
},{
loader: 'less-loader'
}]
}]
Когда мы были рады собраться и увидеть эффект, мы обнаружили, что стиль остался прежним, и ничего не изменилось.
Это связано с тем, что у postcss есть только две основные функции: первая — преобразовать CSS в абстрактное синтаксическое дерево AST, которым можно манипулировать с помощью JS, а вторая — вызвать плагины для обработки AST и получения результатов; поэтому postcss обычно обрабатывает CSS через плагины и не будет обрабатывать его напрямую, поэтому сначала нам нужно установить некоторые плагины:
npm i -D autoprefixer postcss-plugins-px2rem cssnano
.browserslistrc
> 0.25%
last 2 versions
postcss.config.js
module.exports = {
plugins: [
//自动添加前缀
require('autoprefixer'),
//px转为rem,应用于移动端
require('postcss-plugins-px2rem')({ remUnit: 75 }),
//优化合并css
require('cssnano'),
]
}
autoprefixer
babel-loader
npm i -D babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime
npm i -S @babel/runtime
{
//省略其他配置
rules: [{
test: /\.js/,
use: {
loader: 'babel-loader'
}
}]
}
.babelrc
{
"presets": [
"@babel/preset-env"
],
"plugins": [
"@babel/plugin-transform-runtime"
]
}
{
//省略其他配置
rules: [{
test: /\.js$/,
use: {
loader: 'babel-loader'
},
// exclude: /node_modules/,
include: [path.resolve(__dirname, 'src')]
}]
}
$
npm i file-loader url-loader -D
{
//省略其他配置
rules: [{
test: /\.(png|jpg|gif|jpeg|webp|svg|eot|ttf|woff|woff2)$/,
use: {
loader: 'url-loader',
options: {
//10k
limit: 10240,
//生成资源名称
name: '[name].[hash:8].[ext]',
//生成资源的路径
outputPath: 'imgs/'
},
exclude: /node_modules/,
}
}]
}
body{
width: 100vw;
height: 100vh;
background: url(./bg.png) no-repeat;
background-size: 400px 400px;
background-position: center center;
}
html-withimg-loader
npm i -D html-withimg-loader
{
//省略其他配置
rules: [{
test: /\.(htm|html)$/,
use: {
loader: 'html-withimg-loader'
}
}]
}
use: {
loader: 'url-loader',
options: {
//10k
limit: 10240,
esModule: false
}
}
<%= htmlWebpackPlugin.options.title %>
<img src="<%=require('./src/bg1.png') %>" alt="" srcset="">
vue-loader
npm i -D vue-loader vue-template-compiler
npm i -S vue
//src/App.vue
<template>
<div id="app">
<div class="box" @click="tap">{{title}}</div>
</div>
</template>
<script>
export default {
name: 'app',
data(){
return {
title: 'app实例'
}
},
methods: {
tap(){
this.title = this.title.split('').reverse().join('')
}
}
}
</script>
<style>
#app{
font-size: 16px;
background: #ccc;
}
</style>
//src/main.js
import Vue from 'vue'
import App from './App.vue'
new Vue({
render: h => h(App)
}).$mount('#app')
.vue
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
module: {
rules: [
//省略其他loader
{
test: /\.vue$/,
loader: 'vue-loader'
}]
},
plugins: [
new VueLoaderPlugin(),
]
}
static-server
npm i -D webpack webpack-dev-server
webpack.dev.config.js
module.exports = {
//省略其他配置
devServer: {
//启动服务器端口
port: 9000,
//默认是localhost,只能本地访问
host: "0.0.0.0",
//自动打开浏览器
open: false,
//启用模块热替换
hot: true,
//启用gzip压缩
compress: true
},
plugins: [
//热更新插件
new webpack.HotModuleReplacementPlugin({
})
]
}
webpack-dev-server
contentBase: [
path.join(__dirname, "public"),
path.join(__dirname, "assets")
]
cheap-module-eval-source-map
source-map
module.exports = {
devtool: 'cheap-module-eval-source-map',
//其他配置
}
plugins
clean-webpack-plugin
npm i -D clean-webpack-plugin
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
//其他配置
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './public/index.html',
filename: 'index.html',
})
]
}
mini-css-extract-plugin
npm i -D mini-css-extract-plugin
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
//其他配置
module: {
rules: [
{
test: /\.less/,
use: [{
loader: isDev ? 'style-loader' : MiniCssExtractPlugin.loader
},{
loader: 'css-loader'
},{
loader: 'less-loader'
}]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].[hash:8].css",
})
]
}
output.filename
optimize-css-assets-webpack-plugin
production
npm i optimize-css-assets-webpack-plugin -D
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
//其他配置
plugins: [
new OptimizeCSSAssetsPlugin(),
]
}
copy-webpack-plugin
npm i -D copy-webpack-plugin
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: 'public/js/*.js',
to: path.resolve(__dirname, 'dist', 'js'),
flatten: true,
}
]
}),
]
}
ProvidePlugin
import $ from 'jquery'
$('.box').html('box')
$
module.exports = {
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
}),
]
}
【前端壹读】