官方文档:webpack.docschina.org/
webpack高级配置其实就是进行webpack优化,让代码编译运行时更好。
我们从以下角度进行优化:
1、提升开发体验
2、提升打包构建速度
3、减少代码体积
4、优化代码运行性能
SourceMap
为什么?
开发时在浏览器运行的代码是经过webpack编译过的,如下图:
假如程序内有报错,在控制台提示的错误位置是编译后代码的位置,不太友好,而我们想提示源文件的位置。
是什么?
它称之为源代码映射,是一个用来生成源代码与构建后代码---映射的文件的方案。
它会生成一个xxx.map文件,里面包含源代码和构建后代码每一行、每一列的映射关系,当构建后代码出错了,会通过xxx.map文件,从构建后代码出错位置,找到映射后代码出错位置,从而让浏览器提示源代码文件出错位置,帮助我们更快的找到问题根源。
怎么用?
文档链接:webpack.docschina.org/configurati…
devtool文档里有很多方式,但是我们只关注常用的两种模式:
. 开发模式: cheap-module-source-map
优点:打包编译速度快,只包含行映射
缺点:没有列映射
mode: 'development',
// SourceMap
devtool: 'cheap-module-source-map'
. 生产模式: source-map
优点:包含行/列映射
缺点:打包编译速度更慢
mode: 'production',
// SourceMap
devtool: 'source-map'
提升打包构建速度
HotModuleReplacement
为什么?
打包时我们修改一个模块的代码,webpack默认会将所有模块重新打包编译,速度很慢。
是什么?
HotModuleReplacement(HRM/热模块替换):在程序运行中、替换、添加或删除模块,而无需重新加载整个页面。
怎么用?
热模块替换只对css有效
devServer: {
hot: true, // 默认开启
},
如果想让js模块也有热更新,比如想让count.js模块热更新,可以这样处理
import count from './js/count'
if(module.hot) {
module.hot.accept('./js/count') // 当更改count.js内部的代码时就会只更新这一个js模块
}
这样手动写会写很多,vue-loader和react-hot-loader都给我们做好了
OneOf
为什么?
打包时每个文件都会经过所有loader处理,虽然因为test正则原因实际没有处理上,但是都要过一遍,比较慢。
是什么?
顾名思义,就是只能匹配上一个loader,剩下的就不匹配了。
怎么用?
用{ oneOf: []}把所有loader配置包裹起来,这个配置支持开发环境和生产环境。
// 加载器
module: {
rules: [
// loader的配置
{
oneOf: [
// css-loader
{
test: /\.css$/i, // 检测.css结尾的文件
use: [
"style-loader", // 将js中css通过创建style标签添加到html文件生效
"css-loader" // 将css文件编译成commonjs模块到js中
], // use执行顺序从右向左
},
...好多loader
]
}
]
},
Include-Exclude
为什么?
开发时我们需要用到第三方库和插件,所有文件都下载到node_modules中了,而这些文件是不需要要编译可以直接使用的。
是什么?
include:包含 值处理xxx文件
exclude: 排除 出了xxx文件以外其他文件都处理
这两个配置只能写一个,要不包含,要不排除
怎么用?
主要针对js文件
// 配置babel
{
test: /\.js$/,
exclude: /node_modules/, // 排除node_modules文件,其它文件都处理
// include: path.resolve(__dirname, '../src'), // 只处理src下的文件,其它文件不处理
use: {
loader: 'babel-loader'
}
}
eslint插件配置:
// eslint配置
new ESLintPlugin({
// 检测的目录
context: path.resolve(__dirname, '../src'),
exclude: ['node_modules'], // 排除node_modules文件(默认会排除node_modules)
}),
Cache
为什么?
每次打包时js文件都会经过Eslint检查和babel编译,速度比较慢,
我们可以缓存之前的eslint检查和babel编译结果,这样第二次打包时速度就会更快了。
是什么?
对eslint检查和babel编译结果进行缓存。
怎么用?
对eslint和babel开启缓存:
// 配置babel
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true, // 开启babel缓存
cacheCompression: false, // 关闭缓存文件压缩
}
}
}
plugins
// eslint配置
new ESLintPlugin({
context: path.resolve(__dirname, '../src'),
exclude: ['node_modules'],
cache: true, // 开启eslint缓存
cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'), // 设置eslint缓存目录
}),
Thead
为什么?
当项目越来越庞大时,打包速度越来越慢,甚至一个下午才能打包出来,这个速度是比较慢的。
我们想要提升打包速度,其实就是提升js打包速度,因为其他文件都比较少,
而对js处理,主要就是eslint、babel、Terser三个工具,所以我们要提升他们的运行速度。
我们可以开启很多进程同时处理js文件,这样速度就比之前单进程打包速度更快了。
是什么?
多进程打包:开启电脑的多个进程同时干一件事,速度更快。
注意:请仅在特别耗时的操作中使用,因为每个进程启动就有大约为600ms左右开销。
怎么用?
我们启动进程的数量就是我们cpu的核数。
1、如何获电脑cpu的核数?
const os = require('os');
const threads = os.cpus().length;
console.log(threads, 'threads')
2、下载包
npm i thread-loader -D
3、thread-loader要放在babel-loader前面,
我们分别对处理js的三个库babel、eslint、terser开启多进程
const os = require('os'); // 内置
const TerserWebpackPlugin = require('terser-webpack-plugin'); // 内置(压缩js)
const threads = os.cpus().length;
// 配置babel
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'thread-loader', // 开启多进程,对babel做处理
options: {
workers: threads, // 设置进程数量
}
},
{
loader: 'babel-loader',
options: {
// presets: ['@babel/preset-env']
cacheDirectory: true,
cacheCompression: false,
}
}
]
}
eslint开启多进程
// eslint配置
new ESLintPlugin({
// 检测的目录
context: path.resolve(__dirname, '../src'),
exclude: ['node_modules'],
cache: true,
cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'),
threads, // 开启多进程和设置进程数量
}),
terser开启多进程
optimization: {
// webpack5推荐把压缩放这里
minimizer: [
// css压缩
new CssMinimizerPlugin(),
// js压缩配置(生产环境默认开启)
new TerserWebpackPlugin({
parallel: threads, // 开启多进程和设置进程数量
})
]
},
减少代码体积
Tree Shaking 去除没用到的代码
为什么?
开发时我们引用第三方工具函数库或组件库。如果没有特殊处理的话,打包时会引入整个库,可实际上我们可能只用到了极小部分的功能,这样整个库打包出来,体积就太大了。
是什么?
tree shaking 是一个术语,通常用于描述javascript中没有使用上的代码。
注意:它依赖ES Moudle
怎么用?
webpack默认开启了这个功能,无需额外配置。
babel 公共方法提取
为什么?
babel为编译代码都插入了辅助代码,使代码体积变大。
babel对一些公共代码使用了非常小的辅助代码,比如_extend,默认情况下会被定义在每一个需要他的文件中。
你可以将这些公共方法作为一个独立模块,来避免重复定义。\
是什么?\
@babel/plugin-transform-runtime : 禁用了babel对每个文件的runtime注入,而是引入
@babel/plugin-transform-runtime,并且使用所有辅助代码从这里引用。
怎么用?
1.下载包
npm install @babel/plugin-transform-runtime -D
2.配置
// 加载器
module: {
rules: [
// loader的配置
// 配置babel
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'thread-loader',
options: {
workers: threads,
}
},
{
loader: 'babel-loader',
options: {
cacheDirectory: true,
cacheCompression: false,
plugins: ['@babel/plugin-transform-runtime'] , // 减少代码体积,开发/生产环境都能用
}
}
]
}
]
}
]
},
Image Minimizer
为什么?
开发如果引入的图片比较多,那么图片体积会比较大,将来请求速度比较慢。
我们可以对图片进行压缩,减少图片体积。
注意:如果项目中图片都是在线链接,那么就不需要了,本地项目静态图片才需要压缩。
是什么?\
image-minimizer-webpack-plugin:用来压缩图片的插件
怎么用?
1.下载包:
npm install image-minimizer-webpack-plugin imagemin -D
还有剩下的包需要下载,有两种模式:
1、无损压缩
npm install imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D
2、有损压缩
npm install imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo -D
2.配置
以无损压缩为例
optimization: {
// webpack5推荐把压缩放这里
// 压缩图片(也可以放到plugin配置中)
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminGenerate,
options: {
plugins: [
["gifsicle", { interlaced: true }],
["jpegtran", { progressive: true }],
["optipng", { optimizationLevel: 5 }],
[
"svgo",
{
plugins: [
"preset-default",
"prefixIds",
{
name: "sortAttrs",
params: {
xmlnsOrder: "alphabetical"
}
}
]
}
]
]
}
}
})
]
},
优化代码性能
Code Split
为什么?
打包文件时会把所有js代码打包到一个文件中,体积太大了,我们如果只要渲染首页,就应该只加载首页的js文件,其它文件不应该加载。
所以我们应该将打包生成的文件进行代码分割,生成多个js文件,渲染哪个页面,就只加载某个js文件,这样加载的资源就少,速度就更快。
是什么?
code split 主要做两件事:
1、分割文件,将打包生成的文件进行分割,生成多个js文件。
2、按需加载,需要哪个文件就加载哪个文件。
怎么用?
代码分割实现方式有不同的方式\
1、多入口对应多输出
我们配置两个入口main.js和app.js
依赖下载:
npm i webpack webpack-cli html-webpack-plugin -D
webpack配置:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'production',
// entry: './src/main.js', // 单入口
entry: { // 多入口
app: './src/app.js',
main: './src/main.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js', // webpack的命名方式, [name]:以文件名自己命名
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'public/index.html')
})
]
}
2、多入口提取公共模块
webpack配置:
optimization: {
// 代码分割配置
splitChunks: {
chunks: 'all', // 对所有模块进行分割
// 以下是默认配置
// minSize: 20000, // 分割代码最小的大小(分割的最小体积是20kb)
// minRemainingSize: 0, // 类似于minSize,最后确保提取的文件大小不能为0(带下为0的模块没必要提取)
// minChunks: 1, // 至少被引用的次数,满足条件才会代码分割
// maxAsyncRequests: 30, // 按需加载时并行加载的文件的最大数量(请求数量大于30时,就不会再分割代码了,并行请求数量大对服务器的压力变大)
// maxInitialRequests: 30, // 入口js文件最大并行请求数量(初始请求数量不能超过30个)
// enforceSizeThreshold: 50000, // 超过50kb一定会单独打包(此时会忽略minRemainingSize、maxAsyncRequests、maxInitialRequests)
// cacheGroups: { // 组,哪些模块要打包到一个组
// defaultVendors: { // 组名
// test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块
// priority: -10, // 权重(越大越高)
// reuseExistingChunk: true, // 如果当前chunk包含已从主bundle中拆分出的模块,则它将被重用,而不是生成新的模块
// },
// default: { // 其它没有写的配置会使用上面的默认值
// minChunks: 2, // 这里的minChunks权重更大
// priority: -20,
// reuseExistingChunk: true,
// }
// }
// 修改配置
cacheGroups: {
// 组,哪些模块要打包到一组
// defaultVendors: { // 组名
// test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块
// priority: -10, // 权重(越大越高)
// reuseExistingChunk: true, // 如果当前chunk包含已从主bundle中拆分出的模块,则它将被重用,而不是生成新的模块
// },
default: {
// 其它没有写的配置会使用上面的默认值
minSize: 0, // 我们定义的文件体积太小了,所以要改最小的打包文件体积
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
}
}
}
}
有了上面代码分割的配置,有几个入口就会打包成多少个文件,有多少个按需加载就会打包多少个文件,公共代码块也会根据规则打包生成单独文件。
3、多入口按需加载
点击按钮的时候才会加载对应的js模块(加载后将不会加载)
document.getElementById('btn').onclick = function () {
import('./count').then((res) => {
console.log('模块加载成功', res.default(2,2))
}).catch((err) => {
console.log('模块加载失败',err)
})
}
4、单入口
开发时,我们可能是单页面应用(SPA),只有一个入口(单入口);
optimization: {
// 代码分割配置
splitChunks: {
chunks: "all",
// 其它的都用默认值即可(由于只有一个入口)
// 不需要配置cacheGroup相关配置了,因为只有一个入口,其它的配置沿用默认值
}
},
5、code split 给模块命名
按需加载时,定义好模块名:
document.getElementById('btn').onclick = function() {
// /* webpackChunkName: "math" */ webpack魔法命名
import(/* webpackChunkName: "math" */ './js/math').then(res => {
console.log(res.mul(3,4), '按需加载')
})
}
webpack配置:
output: {
path: path.resolve(__dirname, '../dist'),
// 入口文件名
filename: 'static/js/main.js',
// 除入口文件外其它代码块儿的命名
chunkFilename: 'static/js/[name].js',
clean: true
},
6、统一命名配置
我们之前配置了,入口文件、其它模块、图片、字体图标、样式命名,下面来统一配置这些文件的命名
入口文件、其它模块、其它资源的命名都可以在output中配置
output: {
// 所有文件输出路径
path: path.resolve(__dirname, '../dist'), // 要求用绝对路径
// 入口文件打包输出的文件名
filename: 'static/js/[name].[contenthash:8].js', // 把static/js/main.js改成static/js/[name].js,如果将来入口文件名称变了,还可以兼容
// 除入口文件外其它代码块儿的命名
chunkFilename: 'static/js/[name].chunk.[contenthash:8].js', // 文件名后加上.chunk可以区分那个是入口文件,哪些是模块文件
// 图片字体等,通过type:asset 处理资源命名方法
assetModuleFilename: 'static/media/[hash:10][ext][query]',
},
样式文件命名:
plugins: [
// 提取css成单独文件
new MiniCssExtractPlugin({
// 改变css文件输出后的路径
filename: 'static/css/[name].css',
// 模块命名 (如果有动态导入的css,也会打包成单独模块)
chunkFilename: 'static/css/[name].chunk.[contenthash:8].css',
}),
]
preload/prefetch
为什么?
我们已经做过了代码分割,也用了import动态导入来实现按需加载,
但是还不够好,当用户点击时,如果动态导入的文件很大,就会出现卡顿。
我们想在浏览器空闲时加载后续需要的资源,就需要用到preload或prefetch技术。
是什么?\
preload:告诉浏览器立即加载资源
prefetch: 告诉浏览器在空闲时才开始加载资源
他们的共同点:
1.都只会加载资源,并不执行
2.都有缓存
他们的区别:
preload:加载优先级高; prefetch:加载优先级低
preload:只能加载当前页面需要使用的资源,prefetch可以加载当前页面使用的资源,也可以加载下一个页面需要的资源
总结:
当前页面优先级高的资源用preload加载
下一个页面需要的资源用prefetch加载
共同存在的问题:
他们的兼容性很差,从can i use网站看,preload的兼容性要稍微好些。\
@vue/preload-webpack-plugin 插件可以将资源自动变成prelaod或prefetch的方式加载
下载插件:
npm install @vue/preload-webpack-plugin -D
webpack配置:
const PreloadWebpackPlugin = require('@vue/preload-webpack-plugin');
plugins: [
// plugin的配置
new PreloadWebpackPlugin({
rel: 'preload', // js用preload的方式去加载,也可以用prefetch
as: 'script', // 作为script标签的优先级去执行
// as: 'style', // 如果想要优先级最高,可以设置为style
})
],
缓存处理
为什么?
上面我们用了[contenthash]来保证每次打包,每个文件的名称变化,来刷新缓存。
如果A依赖b、c、d模块,如果b模块改了一点代码,那么a文件打包出来的文件的hash值也会变化,其实只更新b模块就行了,这样有助于打包速度。
怎么做?
可以让文件名放到runtime文件中,这样即使依赖文件更新,a也不会重新打包;
webpack配置:
optimization: {
// runtime文件来保存文件的hash值
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}.js`
}
},
解决js兼容问题 CoreJS
为什么?
过去我们用bable对js进行兼容性处理,其中使用@babel/preset-env智能预设来处理兼容性处理。
他能将es6的语法进行编译转换,比如箭头函数,点点点运算符等,但是如果是async函数promise对象,数组的一些方法includes等,它就没法处理。
所以此时我们的代码仍然存在兼容性问题,一旦遇到低版本浏览器会直接报错,想把js兼容问题彻底解决,需要用到core-js
是什么?
core-js是专门用来做es6以及以上api的polyfill。
polyfill翻译过来叫做垫片、补丁。就是用社区上提供的一段代码,让我们在不兼容某些新特性的浏览器上,使用该特性。
怎么用?
1、完整引入:
下载依赖
npm i core-js
入口文件引入core-js
import 'core-js'
2、按需引入
// 按需引入
import 'core-js/es/promise'
3、自动引入
需要配置babel.config.js
module.exports = {
// 预设
presets: [
// '@babel/preset-env' // 智能预设,允许使用更高级的js语法
['@babel/preset-env', {
useBuiltIns: 'usage', // 按需引入core-js
corejs: 3
}]
]
}
pwa
为什么?
开发web app项目,项目一旦处于网络离线情况,就没法访问了。
我们希望给项目提供离线体验。
是什么?
渐进式网络应用程序(progressive web application PWA):是一种可以提供类似native app(原生应用程序)体验的web app的技术,期中最重要的是,在离线(offline)时,应用程序可以继续运行功能。
内部通过service workers技术实现的。
怎么用?
下载包
npm install workbox-webpack-plugin --save-dev
引入
const WorkboxPlugin = require('workbox-webpack-plugin');
配置
plugins: [
new WorkboxPlugin.GenerateSW({
// 这些选项帮助快速启用 ServiceWorkers
// 不允许遗留任何“旧的” ServiceWorkers
clientsClaim: true,
skipWaiting: true,
}),
],
在main.js中假如一段代码,注册生成service worker
// 注册生成service worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js').then(registration => {
console.log('SW registered: ', registration);
}).catch(registrationError => {
console.log('SW registration failed: ', registrationError);
});
});
}
截图来自尚硅谷
开发环境配置:
const ESLintPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const os = require('os'); // 内置
const TerserWebpackPlugin = require('terser-webpack-plugin'); // 内置(压缩js)
const threads = os.cpus().length;
module.exports = {
// 入口
entry: './src/main.js', // 用相对路径
// 出口
output: {
// 所有文件输出路径
path: path.resolve(__dirname, '../dist'), // 要求用绝对路径(开发模式没有输出,这里可以省去)
// 入口文件打包输出的文件名
filename: 'static/js/[name].js', // 把static/js/main.js改成static/js/[name].js,如果将来入口文件名称变了,还可以兼容
// 除入口文件外其它代码块儿的命名
chunkFilename: 'static/js/[name].chunk.js', // 文件名后加上.chunk可以区分那个是入口文件,哪些是模块文件
// 图片字体等,通过type:asset 处理资源命名方法
assetModuleFilename: 'static/media/[hash:10][ext][query]',
// 自动清除上次打包的内容
// 原理:在打包前,将整个path目录清空,再进行打包
clean: true , // 开发模式没有输出,可以不配置
},
// 加载器
module: {
rules: [
// loader的配置
{
oneOf: [
// css-loader
{
test: /\.css$/i, // 检测.css结尾的文件
use: [
"style-loader", // 将js中css通过创建style标签添加到html文件生效
"css-loader" // 将css文件编译成commonjs模块到js中
], // use执行顺序从右向左
},
{
test: /\.less$/i,
use: [
// compiles Less to CSS
'style-loader',
'css-loader',
'less-loader',
],
},
{
test: /\.s[ac]ss$/i,
use: [
'style-loader',
'css-loader',
// 将 Sass 编译成 CSS
'sass-loader',
],
},
{
test: /\.styl$/i,
use: [
'style-loader',
'css-loader',
// 将 stylus 编译成 CSS
'stylus-loader',
],
},
{
// 只需要打开此配置就行,不需要下载额外loader
// 如果不对图片做额外优化可以没有此配置
test: /.(png|jpe?g|gif|webp|svg)$/,
type: "asset",
parser: {
dataUrlCondition: {
// 小于10kb的图片会转base64
// 有点:减少请求数量。缺点:小图的体积会变大一点点(大图的体积会变大很多,这里一般限制10kb一下的图片转base64)
maxSize: 10 * 1024 // 10kb
}
},
generator: {
// 输出图片名称
// 将图片输出在static/images文件下
// filename: 'static/images/[hash][ext][query]'
// [hash:10] 代表hash值只取前10位
// filename: 'static/images/[hash:10][ext][query]'
}
},
{
// 打包字体资源
// 若不需要进行额外配置,不需要进行针对media做任何配置,webpack会原封不动的输出出去
// 如果需要打包mp3、MP4、avi等音视频资源,webpack识别不了他们,只需要在后面加配置即可
test: /.(ttf|woff2?|mp3|mp4|avi)$/,
type: "asset/resource",
generator: {
// 改变资源输出后的路径
// filename: 'static/media/[hash:10][ext][query]'
}
},
// 配置babel
{
test: /\.js$/,
exclude: /node_modules/, // 排除node_modules文件,其它文件都处理
// include: path.resolve(__dirname, '../src'), // 只处理src下的文件,其它文件不处理
use: [
{
loader: 'thread-loader', // 开启多进程,对babel做处理
options: {
workers: threads, // 指定开启多少个进程
}
},
{
loader: 'babel-loader',
options: {
// presets: ['@babel/preset-env']
cacheDirectory: true, // 开启babel缓存
cacheCompression: false, // 关闭缓存文件压缩
plugins: ['@babel/plugin-transform-runtime'] , // 减少代码体积
}
}
]
}
]
}
]
},
// 插件
plugins: [
// plugin的配置
// eslint配置
new ESLintPlugin({
// 检测的目录
context: path.resolve(__dirname, '../src'),
exclude: ['node_modules'], // 排除node_modules文件(默认会排除node_modules)
cache: true, // 开启eslint缓存
cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'), // 设置eslint缓存目录
threads, // 开启多进程和设置进程数量
}),
new HtmlWebpackPlugin({
// 模板,以public/index.html为模板,创建新的html文件
// 新html文件的特点:1、和原来结构一致,2、自动引入打包输出的资源
template: path.resolve(__dirname, '../public/index.html')
})
],
// 开发服务器(开发服务器是不会输出资源的,它是在内存中编译打包的)
devServer: {
host: 'localhost', // 启动服务器的域名
port: 3000, // 启动服务器端口号
open: false, // 是否自动打开浏览器
open: true,
hot: true, // 默认开启
},
// 模式
mode: 'development',
// SourceMap
devtool: 'cheap-module-source-map'
}
生产环境配置:
const ESLintPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
// const PreloadWebpackPlugin = require('@vue/preload-webpack-plugin');
const WorkboxPlugin = require('workbox-webpack-plugin');
const path = require('path');
const os = require('os'); // 内置
const TerserWebpackPlugin = require('terser-webpack-plugin'); // 内置(压缩js)
const threads = os.cpus().length;
console.log(threads, 'threads')
// 获取处理样式的loader
const getStyleLoader = (pre) => {
return [
// compiles Less to CSS
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'postcss-preset-env', // 能解决大多数样式兼容性问题
{
// 其他选项
},
],
],
},
},
},
pre,
].filter(Boolean)
}
module.exports = {
// 入口
entry: './src/main.js', // 用相对路径
// 出口
output: {
// 所有文件输出路径
path: path.resolve(__dirname, '../dist'), // 要求用绝对路径
// 入口文件打包输出的文件名
filename: 'static/js/[name].[contenthash:8].js', // 把static/js/main.js改成static/js/[name].js,如果将来入口文件名称变了,还可以兼容
// 除入口文件外其它代码块儿的命名
chunkFilename: 'static/js/[name].chunk.[contenthash:8].js', // 文件名后加上.chunk可以区分那个是入口文件,哪些是模块文件
// 图片字体等,通过type:asset 处理资源命名方法
assetModuleFilename: 'static/media/[hash:10][ext][query]',
// 自动清除上次打包的内容
// 原理:在打包前,将整个path目录清空,再进行打包
clean: true
},
// 加载器
module: {
rules: [
// loader的配置
{
oneOf: [
// css-loader
{
test: /\.css$/i, // 检测.css结尾的文件
use: getStyleLoader(),
},
{
test: /\.less$/i,
use: getStyleLoader('less-loader'),
},
{
test: /\.s[ac]ss$/i,
use: getStyleLoader('sass-loader'),
},
{
test: /\.styl$/i,
use: getStyleLoader('stylus-loader'),
},
{
// 只需要打开此配置就行,不需要下载额外loader
// 如果不对图片做额外优化可以没有此配置
test: /.(png|jpe?g|gif|webp|svg)$/,
type: "asset",
parser: {
dataUrlCondition: {
// 小于10kb的图片会转base64
// 有点:减少请求数量。缺点:小图的体积会变大一点点(大图的体积会变大很多,这里一般限制10kb一下的图片转base64)
maxSize: 10 * 1024 // 10kb
}
},
generator: {
// 输出图片名称
// 将图片输出在static/images文件下
// filename: 'static/images/[hash][ext][query]'
// [hash:10] 代表hash值只取前10位
// filename: 'static/images/[hash:10][ext][query]'
}
},
{
// 打包字体资源
// 若不需要进行额外配置,不需要进行针对media做任何配置,webpack会原封不动的输出出去
// 如果需要打包mp3、MP4、avi等音视频资源,webpack识别不了他们,只需要在后面加配置即可
test: /.(ttf|woff2?|mp3|mp4|avi)$/,
type: "asset/resource",
generator: {
// 改变资源输出后的路径
// filename: 'static/media/[hash:10][ext][query]'
}
},
// 配置babel
{
test: /\.js$/,
exclude: /node_modules/, // 排除node_modules文件
use: [
{
loader: 'thread-loader', // 开启多进程,对babel做处理
options: {
workers: threads, // 指定开启多少个进程
}
},
{
loader: 'babel-loader',
options: {
// presets: ['@babel/preset-env']
cacheDirectory: true, // 开启babel缓存
cacheCompression: false, // 关闭缓存文件压缩
plugins: ['@babel/plugin-transform-runtime'] , // 减少代码体积
}
}
]
}
]
}
]
},
// 插件
plugins: [
// plugin的配置
// eslint配置
new ESLintPlugin({
// 检测的目录
context: path.resolve(__dirname, '../src'),
exclude: ['node_modules'], // 排除node_modules文件(默认会排除node_modules)
cache: true, // 开启eslint缓存
cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'), // 设置eslint缓存目录
threads, // 开启多进程和设置进程数量
}),
new HtmlWebpackPlugin({
// 模板,以public/index.html为模板,创建新的html文件
// 新html文件的特点:1、和原来结构一致,2、自动引入打包输出的资源
template: path.resolve(__dirname, '../public/index.html')
}),
// 提取css成单独文件
new MiniCssExtractPlugin({
// 改变css文件输出后的路径
filename: 'static/css/[name].[contenthash:8].css',
// 模块命名 (如果有动态导入的css,也会打包成单独模块)
chunkFilename: 'static/css/[name].chunk.[contenthash:8].css',
}),
// // css压缩
// new CssMinimizerPlugin(),
// // js压缩配置(生产环境默认开启)
// new TerserWebpackPlugin({
// parallel: threads, // 开启多进程和设置进程数量
// })
// new PreloadWebpackPlugin({
// rel: 'preload', // js用preload的方式去加载,
// as: 'script', // 作为script标签的优先级去执行
// // as: 'style', // 如果想要优先级最高,可以设置为style
// }),
new WorkboxPlugin.GenerateSW({
// 这些选项帮助快速启用 ServiceWorkers
// 不允许遗留任何“旧的” ServiceWorkers
clientsClaim: true,
skipWaiting: true,
}),
],
optimization: {
// webpack5推荐把压缩放这里
minimizer: [
// css压缩
new CssMinimizerPlugin(),
// js压缩配置(生产环境默认开启)
new TerserWebpackPlugin({
parallel: threads, // 开启多进程和设置进程数量
}),
// 压缩图片(也可以放到plugin配置中)
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminGenerate,
options: {
plugins: [
["gifsicle", { interlaced: true }],
["jpegtran", { progressive: true }],
["optipng", { optimizationLevel: 5 }],
[
"svgo",
{
plugins: [
"preset-default",
"prefixIds",
{
name: "sortAttrs",
params: {
xmlnsOrder: "alphabetical"
}
}
]
}
]
]
}
}
})
],
// 代码分割配置
splitChunks: {
chunks: "all",
// 其它的都用默认值即可
},
// runtime文件来保存文件的hash值
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}.js`
}
},
// 模式
mode: 'production',
// SourceMap
devtool: 'source-map'
}
package.json
{
"name": "webpack5",
"version": "1.0.0",
"description": "{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}",
"main": "note.js",
"scripts": {
"start": "npm run dev",
"dev": "npx webpack server --config ./config/webpack.dev.js",
"build": "webpack --config ./config/webpack.prod.js"
},
"repository": {
"type": "git",
"url": "https://gitee.com/xxx/webpack5.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.20.2",
"@babel/plugin-transform-runtime": "^7.19.6",
"@babel/preset-env": "^7.20.2",
"babel-loader": "^9.1.0",
"css-loader": "^6.7.1",
"css-minimizer-webpack-plugin": "^4.2.2",
"eslint": "^8.26.0",
"eslint-webpack-plugin": "^3.2.0",
"html-webpack-plugin": "^5.5.0",
"image-minimizer-webpack-plugin": "^3.8.0",
"imagemin": "^8.0.1",
"imagemin-gifsicle": "^7.0.0",
"imagemin-jpegtran": "^7.0.0",
"imagemin-mozjpeg": "^10.0.0",
"imagemin-optipng": "^8.0.0",
"imagemin-pngquant": "^9.0.2",
"imagemin-svgo": "^10.0.1",
"less": "^4.1.3",
"less-loader": "^11.1.0",
"mini-css-extract-plugin": "^2.6.1",
"postcss": "^8.4.18",
"postcss-loader": "^7.0.1",
"postcss-preset-env": "^7.8.2",
"sass": "^1.55.0",
"sass-loader": "^13.1.0",
"style-loader": "^3.3.1",
"stylus-loader": "^7.1.0",
"thread-loader": "^3.0.4",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.11.1",
"workbox-webpack-plugin": "^6.5.4"
},
"browserslist": [
"last 2 version",
"> 1%",
"not dead"
],
"dependencies": {
"core-js": "^3.26.0"
}
}