webpack基础
环境要求
- webpack版本:5.1.4
- node版本:20.15.1
- 因版本差异,相关配置会有一定出入
安装本地的webpack
npm i webpack webpack-cli -D
webpack默认配置
- 默认可以不用配置,在src下新建一个index.js文件(默认),执行npx webpack即可打包
- 支持我们的js模块化-CommonJS和ES Module规范
// a.js
export default 'webpack'
// index.js
import str from './a.js'
console.log(str)
// 验证打包,创建index.html文件,引入打包后的js文件,即可验证
webpack手动配置
- 默认配置文件的名字是webpack.config.js
- 自定义配置文件webpack.config.customer.js
const path = require('path')
module.exports = {
mode: 'production',
performance: {
maxEntrypointSize: 50000000, // 打包体积限制,若hints打开,超出有警告或报错
maxAssetSize: 30000000, // 打包体积限制,若hints打开,超出有警告或报错
hints: false // warning、error、false(boolean),打开/关闭提示
},
entry: { // 配置多入口
'home': './src/index.js',
'other': './src/other.js'
},
output: {
filename: 'js/[name].[contenthash:8].js', // webpack5以下版本用[hash]
path: path.resolve(__dirname, 'dist'), //路径必须是一个绝对路径
publicPath: '', // 会在路径前面添加上相应的字符串
clean: true, // 在生成文件之前清空 output 目录,webpack5以下需要使用clean-webpack-plugin插件
},
}
- 通过配置脚本运行(package.json)
# package.json配置
"build":"webpack --config webpack.config.customer.js"
# 运行
npm run build
# package.json配置
"build":"webpack"
npm run build -- --config webpack.config.customer.js
webpack-dev-server
-
将文件写到内存中,提高打包速度
-
安装及使用
# 安装
npm install webpack-dev-server -D
# 使用
npx webpack-dev-server
- 通过配置脚本运行(package.json)
# 配置
"dev": "webpack-dev-server"
# 使用
npm run dev
- webpack.config.js配置
devServer: {
port: 3001, // 服务端口
host: '0.0.0.0', // 服务器可以被外部访问
client: {
progress: true, // 在浏览器中以百分比显示编译进度
},
static: './dist/', // 从目录提供静态文件的选项(默认是 'public' 文件夹)
compress: true, // 是否启用gzip压缩
hot: true,
proxy: [{
context: ['/api'],
target: 'http://localhost:3000',
}],
},
devtool:'source-map'
- 源码报错映射-devtool,开发环境推荐使用eval-source-map, 生产环境推荐使用source-map
devtool:‘source-map’ 源码映射,会单独生成一个sourcemap文件,出错了,会标识当前错误的列和行;
devtool:‘eval-source’ 不会产生单独的文件,但是可以显示行和列
devtool:‘cheap-module-source-map’ 不会产生列,但是是一个单独的映射文件产生后你可以保留起来
devtool:‘cheap-module-eval-source-map’ 不会产生文件,集成在打包后的文件中,也不会产生列
html-webpack-plugin
- 安装方式
npm i html-webpack-plugin -D
- webpack.config.js配置
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
//放着所有的webpack插件
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
//对打包的html页面进行最小化操作
minify: {
removeAttributeQuotes: true, //删除属性的双引号
collapseWhitespace: true //所有代码一行显示,折叠空行
},
hash: true //在引入js文件时,在路径后面添加hash戳
}),
]
webpack常用loader
webpack样式处理
- 处理css相关的loader及规则
# css-loader,处理@import这种语法
# style-loader 他是把css插入到head的标签中
# loader的特点,希望单一
# loader的用法,字符串只用一个loader,多个loader需要[]
# loader的顺序,默认时从右向左执行,从下往上执行
# loader还可以写成对象的方式
# 处理less文件
npm i less less-loader -D
# 处理sass文件
npm i node-sass sass-loader -D
# 处理stylus文件
npm i stylus stylus-loader -D
- (style-loader)将打包后的css内容添加到head头部,以style标签的形式包裹(不推荐)
# 处理样式文件
npm i style-loader -D
- (style-loader)webpack.config.js增加文件处理规则
module: {
rules: [{
test: /\.css$/,
use: [
{
loader: 'style-loader', options: {
insert: 'head', // head body
injectType: "styleTag" // linkTag
}
}, 'css-loader']
},
{
test: /\.less$/,
use: [
{
loader: 'style-loader', options: {
insert: 'head', // head body
injectType: "styleTag" // linkTag
}
},
'css-loader',
'less-loader'
]
}]
}
- (link标签引入)将css单独打包成一个文件
//使用link标签引入打包后的css文件
npm i mini-css-extract-plugin -D
- (link标签引入)webpack.config.js增加文件处理规则修改
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
module: {
rules: [{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader'
]
}]
}
- (postcss)CSS新特性、样式前缀等优雅降级(postcss-preset-env基于postcss框架,它的工作原理是对你的CSS源码进行解析,然后将尚未被所有浏览器广泛支持的新特性转换为已知的、安全的CSS代码片段。通过这种方式,你可以放心地使用如Grid布局、Flexbox、颜色空间等新功能,而不需要担心浏览器的差异化问题,postcss-preset-env已经内置了autoprefixer的特性)
npm i postcss postcss-loader postcss-preset-env -D
- (postcss)webpack.config.js增加postcss-loader处理
rules: [{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'less-loader'
]
}]
- (postcss)在项目根目录新建一个postcss.config.js文件
module.exports = {
plugins: [
require('postcss-preset-env')({
browsers: [ // 添加前缀的浏览器列表
"> 1%",
"Chrome > 31",
"Firefox > 31",
"ie >= 10",
"last 10 versions"
],
autoprefixer: { grid: true }, // grid布局是否添加前缀
}),
],
};
- 通过以上配置,打包出来的css文件并没有被压缩,如需压缩,则需要安装css-minimizer-webpack-plugin插件,使用该优化后,css文件内容可以压缩了,但是js文件并没有压缩,需手动压缩js
# css压缩,
npm install css-minimizer-webpack-plugin -D
# js压缩
npm i terser-webpack-plugin -D
- webpack.config.js配置项添加
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
optimization: {
minimizer: [
new CssMinimizerPlugin({ // 优化css
parallel: true, // 多进程并发执行,提升构建速度
}),
new TerserJSPlugin({ // 优化js
parallel: true,
terserOptions: {
compress: {
drop_console: false, // 移除console
drop_debugger: true, // 移除debugger
},
},
}),
],
},
webpack静态资源
-
一般项目中url-loader和file-loader结合使用,url-loader可以转换为base64
-
url-loader可以处理我们的图片、音视频、字体等静态资源路径
-
依赖包安装
npm i url-loader file-loader -D
- 当在 webpack 5 中使用旧的 assets loader(如
file-loader
/url-loader
/raw-loader
等)和 asset 模块时,你可能想停止当前 asset 模块的处理,并再次启动处理,这可能会导致 asset 重复,你可以通过将 asset 模块的类型设置为'javascript/auto'
来解决 - webpack.config.js增加解析规则,分类打包静态资源
{
test: /\.(png|jp?g|gif|svg)(\?.*)?$/,
type: 'javascript/auto',
use: [
{
loader: 'url-loader',
options: {
limit: 10 * 1024,
esModule: false,
outputPath: "images"
}
}
],
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
type: 'javascript/auto',
use: [
{
loader: 'url-loader',
options: {
limit: 10 * 1024,
esModule: false,
outputPath: "fonts"
}
}
],
},
{
test: /\.(mp4?|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
type: 'javascript/auto',
use: [
{
loader: 'url-loader',
options: {
limit: 10 * 1024,
esModule: false,
outputPath: "medias"
}
}
],
}
- 在js中使用(一)
import webpackLogo from './assets/images/logo.png'
const img = document.createElement('img')
img.src = webpackLogo
document.body.appendChild(img)
- 在css中使用(二)
@font-face {
font-family: DIGITAL-Regular;
src: url("./assets/fonts/DIGITAL-Regular.ttf");
}
.test-box {
font-family: DIGITAL-Regular;
background: url("./assets/images/logo.png") center bottom no-repeat;
}
- 在html中使用,一般不使用(三)
# 在我们的模板html中使用,需再安装一个loader
npm i html-withimg-loader -D
增加配置项
{
test: /\.html$/,
use: 'html-withimg-loader'
}
在html中使用
<img src="./assets/images/logo.png" />
webpack转换es6语法
- 相关包安装
npm i babel-loader @babel/core @babel/preset-env -D
- webpack.config.js添加loader处理
{
test: /\.m?js$/,
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
},
},
},
- @babel/plugin-transform-runtime使用场景
- 避免全局污染:
当使用 Babel 转译包含新特性的代码(如箭头函数、解构赋值、模板字符串等)时,Babel 通常会生成辅助函数(helper functions)以实现这些特性的向下兼容。如果不使用@babel/plugin-transform-runtime
,这些辅助函数会被直接注入到每个转译后的文件中,可能导致全局作用域被污染。而该插件会将这些辅助函数集中放在一个单独的运行时库(如@babel/runtime
)中,通过模块导入的方式使用,从而避免全局污染。- 按需引入 polyfills:
对于 ECMAScript 标准库中新增的 API(如Promise
、Set
、Map
、Array.from
等),@babel/plugin-transform-runtime
会自动识别并替换为运行时库提供的同名函数,而不是在全局范围内添加 polyfills。这样可以确保只对实际用到的 API 进行 polyfill,减小最终生成代码的体积,提高加载效率。- 保持模块化:
对于使用模块化(如 CommonJS 或 ES modules)的项目,@babel/plugin-transform-runtime
有助于保持模块化的语义。通过引入对应的运行时模块,而不是直接在全局添加 polyfills 或辅助函数,可以确保模块间的独立性和互不影响。
- 依赖包安装(转换插件通常仅在开发中使用,但运行时是生产依赖)
npm i @babel/plugin-transform-runtime -D
npm install @babel/runtime -S
# es6 'abdc'.includes('a')之类的语法不会识别,如果需要兼容,安装以下包
npm i core-js -S
- 增加polyfill,兼容ES6新增的对象原型上的属性
// webpack.config.js
presets: [
["@babel/preset-env", {
useBuiltIns: "entry", // or "usage"
corejs: 3,
}]
]
// 在使用的地方引入
import "core-js/stable";
import "regenerator-runtime/runtime";
'abdc'.includes('a')
webpack插件及其它配置
copy-webpack-plugin
- 是一个第三方的Webpack插件,它允许你在构建过程中复制文件和目录到输出目录。通常,这在构建项目时需要将一些不需要经过Webpack处理的文件(如静态资源、HTML文件、字体文件等)复制到输出目录,以便它们可以在部署后在浏览器中访问
- 安装copy-webpack-plugin
npm i copy-webpack-plugin -D
- 使用copy-webpack-plugin
new CopyWebpackPlugin({
patterns: [
{ from: 'public', to: './' }, // 将public文件夹内的内容复制到打包后的根目录下
],
})
BannerPlugin
- BannerPlugin是webpack内置的一个插件,用于在打包后的文件中添加自定义注释的插件。它可以创建单独的许可文件,将许可信息添加到打包后的文件中,以便其他开发者了解和遵守相关的许可要求
- 使用BannerPlugin
//不需要安装包,只需要引入webpack即可
let webpack = require('webpack');
//配置文件配置
plugins: [
new webpack.BannerPlugin('Copyright (c) 2024-present LiuSong')
]
resolve的使用方法
resolve: {
modules: [path.resolve('node_modules')], // 缩小查找范围
extensions: ['.js', '.css', '.json', '.vue'], // 引入这些类型的文件,可以不用写后缀
alias: { // 别名
'@': path.resolve(__dirname, 'src'),
'@assets': path.resolve(__dirname, 'src/assets'),
}
}
全局变量(webpack、node)
相关变量讲解
cross-env
、NODE_ENV
还有webpack.config.js中设置的mode
、DefinePlugin
"scripts": {
"dev": "cross-env NODE_ENV=development PROXY_ENV=test webpack-dev-server --config build/webpack.config.dev.js"
"build": "cross-env NODE_ENV=production webpack --config build/webpack.config.prod.js"
},
编译环境(node环境),我们在终端执行:
npm run dev
或打包npm run build
都是在编译环境,也就是node环境。webpack.config.js
是运行在编译环境中的。运行环境(浏览器环境),我们写的业务代码等都是在浏览器中运行的,也就是打包之后我们的代码还在的。
webpack.config.js中设置的mode值,只能在运行环境中通过
process.env.NODE_ENV
读取到,在编译环境,webpack配置文件中是读取不到的
module.exports = { mode: 'development' }
在 webpack 4+ 中,不需要做任何设置(其实是webpack自动把变量添加到了
DefinePlugin
中)就可以在代码中读取process.env.NODE_ENV
的值了,在 webpack3及其更低版本中,需要在webpack配置文件中使用 DefinePlugin设置成全局变量,才可以访问得到在编译环境(webpack配置文件中使用)的环境变量,因windows和mac系统的差异,配置方式也就不同,这时就需要借助
cross-env
了总结:
mode(值只能是
none
,development
或production
)和DefinePlugin
设置的变量只能在运行环境(业务代码)中使用在
package.json
中设置的环境变量只能在编译环境中(node环境)中使用
cross-env
是用来解决跨环境的
- 安装cross-env解决跨环境问题
npm install cross-env -D
- DefinePlugin(webpack内置插件)定义运行环境全局变量
一般情况下,我们会将全局变量添加到配置文件中,在打包命令中增加编译环境全局变量,在打包时根据全局变量加载对应的配置文件,将配置文件中的变量注入到node进程中,然后在业务代码中使用
项目目录下新建config文件夹,依次增加dev.env.js、test.env.js、prod.env.js
在文件中增加变量
dev.env.js
module.exports = {ENV_CONFIG: '"dev"'}
est.env.js
module.exports = {ENV_CONFIG: '"test"'}
prod.env.js
module.exports = {ENV_CONFIG: '"prod"'}
package.json修改运行脚本
“dev”: “cross-env ENV_CONFIG=dev webpack-dev-server”,
“build:test”: “cross-env ENV_CONFIG=test webpack”,
“build:prod”: “cross-env ENV_CONFIG=prod webpack”
// webpack.config.js中增加插件配置
const webpack = require('webpack');
const env = require(`./config/${process.env.ENV_CONFIG}.env.js`)
new webpack.DefinePlugin({
_WBPACK_ENV_VARIABLE: JSON.stringify(env) // 变量名称随意起
});
// 业务代码中使用
if(_WBPACK_ENV_VARIABLE.ENV_CONFIG === 'dev') { }
- 经验总结
使用环境变量,能满足我们所有的开发需求,解决环境不一致而引发的问题
在实际的项目开发中,我们还可以拆分我们的打包配置文件,进行更细维度的优化整合
比如拆分配置文件为:webpack.base.js,webpack.dev.js,webpack.prod.js,依据不同的环境进行不同的打包方案
webpack整体配置项
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const webpack = require('webpack');
const env = require(`./config/${process.env.ENV_CONFIG}.env.js`)
module.exports = {
mode: 'production',
performance: {
maxEntrypointSize: 50000000, // 打包体积限制,若hints打开,超出有警告或报错
maxAssetSize: 30000000, // 打包体积限制,若hints打开,超出有警告或报错
hints: false // warning、error、false(boolean),打开/关闭提示
},
devtool: 'source-map', //增加映射文件,可以帮我们调试源代码
entry: { // 配置多入口
'home': './src/index.js',
'other': './src/other.js'
},
output: {
filename: 'js/[name].[contenthash:8].js', // webpack5以下版本用[hash]
path: path.resolve(__dirname, 'dist'), //路径必须是一个绝对路径
publicPath: '', // 会在路径前面添加上相应的字符串
clean: true, // 在生成文件之前清空 output 目录,webpack5以下需要使用clean-webpack-plugin插件
},
devServer: {
port: 3001, // 服务端口
host: '0.0.0.0', // 服务器可以被外部访问
client: {
progress: true, // 在浏览器中以百分比显示编译进度
},
static: './dist/', // 从目录提供静态文件的选项(默认是 'public' 文件夹)
compress: true, // 是否启用gzip压缩
hot: true,
proxy: [{
context: ['/api'],
target: 'http://localhost:3000',
}],
},
plugins: [
//放着所有的webpack插件
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
//对打包的html页面进行最小化操作
minify: {
removeAttributeQuotes: true, //删除属性的双引号
collapseWhitespace: true //所有代码一行显示,折叠空行
},
chunks: ['home'], // 将对应的打包文件对号入座
hash: true //在引入js文件时,在路径后面添加hash戳
}),
new HtmlWebpackPlugin({ // 打包多个html页面
template: './src/index.html',
filename: 'main.html',
chunks: ['other'], // 将对应的打包文件对号入座
hash: true //在引入js文件时,在路径后面添加hash戳
}),
new MiniCssExtractPlugin({
// 此处写成'css/main.css',其它静态资源需根据需要添加publicPath: '../'等
filename: '[name].[fullhash].css' // 低版本使用[hash]
}),
new CopyWebpackPlugin({ // 拷贝静态文件
patterns: [
{ from: 'public', to: './' },
],
}),
new webpack.BannerPlugin('Copyright (c) 2024-present LiuSong'), // 添加版权或作者信息
new webpack.DefinePlugin({ // 定义运行环境全局变量
_WBPACK_ENV_VARIABLE: JSON.stringify(env) // 或 '"production"' 写法
})
],
optimization: {
minimizer: [
new CssMinimizerPlugin({ // 优化css
parallel: true, // 多进程并发执行,提升构建速度
}),
new TerserJSPlugin({ // 优化js
parallel: true,
extractComments: false, //不将注释提取到单独的文件中
terserOptions: {
compress: {
drop_console: false, // 移除console
drop_debugger: true, // 移除debugger
},
},
}),
],
},
module: {
rules: [{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'less-loader'
]
},
{
test: /\.m?js$/,
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [['@babel/preset-env', {
useBuiltIns: "entry",
corejs: 3,
targets: {
chrome: "58",
ie: "11"
}
}]],
plugins: ["@babel/plugin-transform-runtime"]
},
},
},
{
test: /\.(png|jp?g|gif|svg)(\?.*)?$/,
type: 'javascript/auto',
use: [
{
loader: 'url-loader',
options: {
limit: 10 * 1024,
esModule: false,
outputPath: "images"
}
}
],
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
type: 'javascript/auto',
use: [
{
loader: 'url-loader',
options: {
limit: 10 * 1024,
esModule: false,
outputPath: "fonts",
}
}
],
},
{
test: /\.(mp4?|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
type: 'javascript/auto',
use: [
{
loader: 'url-loader',
options: {
limit: 10 * 1024,
esModule: false,
outputPath: "medias"
}
}
],
},
{
test: /\.html$/,
use: 'html-withimg-loader'
}
]
},
resolve: {
modules: [path.resolve('node_modules')], // 缩小查找范围
extensions: ['.js', '.css', '.json', '.vue'], // 引入这些类型的文件,可以不用写后缀
alias: { // 别名
'@': path.resolve(__dirname, 'src'),
'@assets': path.resolve(__dirname, 'src/assets'),
}
}
}