前言
近期学完 哲玄(egg.js开发者) 的大前端课程 Webpack 的配置后,写下此篇。
Webpack 5
webpack 提供很多配置,一般脚手架工具会配置好大部分,不需要开发者再进行配置,但是呢,可以不做但不能不会。
/**
* webpack 配置 示例文件
*/
// 引入 path
const path = require('path');
// 引入 vue-loader
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
// 配置
module.exports = {
// 基础目录,绝对路径,用于从配置中解析入口点(entry point)和 加载器(loader)。
// 一般不配置,默认使用 Node.js 进程的当前工作目录
context: path.resolve(process.cwd(), 'app'),
// 模式 development / production / none , 默认 production
mode: 'development',
// 单入口配置
entry: path.resolve(__dirname, './src/index.js'),
// 单入口配置 等同于上面的配置
entry: {
index: path.resolve(__dirname, './src/index.js'),
},
// 多入口配置
entry: {
index: path.resolve(__dirname, './src/index.js'),
other: path.resolve(__dirname, './src/other.js'),
},
// 出口配置示例
output: {
// 输出路径
path: path.join(process.cwd(), 'app/public/dist'),
// 输出文件名
// js/ 表示 打包到 path (配置的输出路径) 下的 js 目录
// [name] 表示文件名
// [chunkhash:8] 表示 hash 值
filename: 'js/[name]_[chunkhash:8].bundle.js',
// 是否携带凭据启用跨域加载 anonymous 不携带凭据 use-credentials 携带凭据
crossOriginLoading: 'anonymous',
// 该选项的值是以 runtime(运行时) 或 loader(载入时) 所创建的每个 URL 的前缀。
// 因此,在多数情况下,此选项的值都会以 / 结束。
publicPath: '/dist',
},
// 模块配置
module: {
// 规则
rules: [
{
test: /\.vue$/, // 匹配规则
use: 'vue-loader', // 匹配到的文件使用的 loader
},
{
test: /\.js$/,
use: 'babel-loader',
// 只对 src 目录下的文件进行处理
include: path.resolve(process.cwd(), 'src'),
// 不处理 node_modules 目录下的文件
exclude: /node_modules/,
},
{
test: /\.(png|jpe?g|gif|svg)$/,
type: 'asset', // 代替 file-loader 和 url-loader
parser: {
dataUrlCondition: {
// 小于 10kb 转 base64,使用此配置会减少 http 请求开销,但是会增加包体积
// 所以配置的时候需要根据项目实际情况平衡
maxSize: 10 * 1024,
},
},
},
{
test: /\.(eot|ttf|woff|woff2)$/,
type: 'asset',
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.scss$/,
// 执行顺序 从右到左,即先执行 sass-loader 再执行 css-loader 最后执行 style-loader
use: ['style-loader', 'css-loader', 'scss-loader'],
}
],
},
// 解析配置
resolve: {
// 尝试按顺序解析这些后缀名。
// 如果有多个文件有相同的名字,但后缀名不同,
// webpack 会解析列在数组首位的后缀的文件 并跳过其余的后缀。
extensions: ['.js', '.vue', 'scss', 'css'],
alias: {
// 使用 @ 代替 app 目录
// 例如 import xxx from '../../pages/index.vue'
// 转换成 import xxx from '@/pages/index.vue'
'@': path.resolve(process.cwd(), 'src'),
},
},
// 插件
plugins: [
// 将你定义过的其他规则复制并应用到 .vue 文件里
// 例如 /\.js$/ 应用到 .vue 文件里的 <script> 块
// 例如 /\.css$/ 应用到 .vue 文件里的 <style> 块
new VueLoaderPlugin(),
// 暴露第三方库到 window context 上
new webpack.ProvidePlugin({ Vue: 'vue' }),
new webpack.DefinePlugin({
// 启用 Vue 2 的选项式 API
__VUE_OPTIONS_API__: true,
// 禁用 Vue 2 的生产环境工具
__VUE_PROD_DEVTOOLS__: false,
// 禁用 Vue 2 的生产环境 hydration 不匹配的详细信息
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false,
}),
new HtmlWebpackPlugin({
// 设置网页标题 如果使用了 template 则 title 会被忽略
title: 'webpack 示例',
// 生成的 HTML 文件的名称
// 这里的根目录是项目根目录 不是 output 配置中的 path
// 如果不配置则默认在 output 配置中的 path 下
filename: path.resolve(process.cwd(), 'dist/', 'index.html'),
// 使用的模板文件
template: path.resolve(process.cwd(), 'src/index.html'),
// 引入的 js 文件 即将哪些 js 文件引入到 html 中
chunks: ['index']
}),
// 多页面配置 多页面可配置多个 HtmlWebpackPlugin
new HtmlWebpackPlugin({
filename: path.resolve(process.cwd(), 'dist/', 'other.html'),
template: path.resolve(process.cwd(), 'src/other.html'),
chunks: ['other']
})
],
// 优化配置
optimization: {
// 代码分割
splitChunks: {
// 自动生成的文件名的分隔符
automaticNameDelimiter: '-',
// 选择哪些 chunks 进行优化
chunks: 'all',
// 按需加载时的最大并行请求数量
maxAsyncRequests: 5,
// 入口点的最大并行请求数量
maxInitialRequests: 3,
// 缓存组
cacheGroups: {
// 提取 第三方 公共代码
// 例如:a 和 b 都引入了 lodash 那么不做此优化时,a 和 b 都会引入 lodash
// 做此优化时 lodash 会被提取到 vendor.bundle.js 中,a 和 b 会引入 vendor.bundle.js
vendor: {
test: /[\\/]node_modules[\\/]/, // 匹配规则
chunks: 'all', // 匹配的 chunk 类型
name: 'vendor', // 提取的文件名
priority: 10, // 权重 值越大 优先级越高
// 强制提取 忽略上层配置的影响
// 忽略 minSize minChunks maxAsyncRequests maxInitialRequests 这些配置
enforce: true,
reuseExistingChunk: true, // 复用已存在的模块
},
// 提取 业务 公共代码
// 例如:a 和 b 都引入了 tools.js 那么不做此优化时,a 和 b 都会引入 tools.js
// 做此优化时 tools.js 会被提取到 common.bundle.js 中,a 和 b 会引入 common.bundle.js
common: {
name: 'common',
chunks: 'all',
minSize: 20000, // 最小尺寸 单位 byte
minChunks: 2, // 最小引用次数
priority: 0,
}
}
},
},
// 其他配置
externals: {
// 提取第三方库 不打包到 bundle.js 中
// 例如:在html中使用 script 标签引入 jQuery CDN
// 然后配置 externals 引入 jQuery
// 当你在代码中使用 import $ from 'jQuery' 时,
// Webpack 会导出全局作用域下的 jQuery 变量,这个变量在 script 标签引入的 jQuery CDN 中。
// 而不需要将 jQuery 打包到 bundle.js 中。
jquery: 'jQuery',
},
// 开发工具 映射代码生成 .map 文件用于错误提示,可选映射到具体行,列。也可关闭,影响打包速度
devtool: 'cheap-module-source-map',
}
优化
前端逃不掉的就是兼容,优化。在 Webapck 中我们需要关注两个主要优化点:打包后的性能,打包速度。 Webpack 提供了很多插件可以用来优化以及其他优化手段,例如 :
- css 压缩(CssMinimizerPlugin)
- 提取 css 并按需引入(MiniCssExtractPlugin)
- 合并 chunk 限制打包数量,减少 http 请求开销(MinChunkSizePlugin,LimitChunkCountPlugin)
- 压缩图片资源(ImageMinimizerWebpackPlugin)
- 多开 worker 进行多线程打包(thread-loader)
- 在module.rules中配置loader时,可配置处理的目录(include)与不处理的目录(exclude)
- 减少生成 chunk 的 hash 长度,这样可以减少引入文件的代码长度。
- 甚至减少使用 loader 与 plugin 都可以达到优化打包速度的目的,毕竟这些都有启动时间。