1、开发配置
const { Configuration } = require('webpack')
const path = require('path')
// 导入 eslint 插件
const ESLintPlugin = require('eslint-webpack-plugin')
// 导入 html插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
// webpack5 基本配置
/**
* @type {Configuration}
*/
module.exports = {
// 入口
entry: './src/main.js',
// 输出
output: {
path: undefined,
filename: 'static/js/bundle.js',
clean: true,
},
// 开发模式
mode: 'development',
// 添加开发环境代码映射
devtool: 'eval-source-map',
// 别名
resolve: {
alias: {
'@': path.resolve(__dirname, '../src'),
},
},
// 加载器
module: {
rules: [
{
oneOf: [
// 处理.js文件,进行兼容性处理
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
// 开启babel缓存
cacheDirectory: true,
// 关闭缓存文件压缩
cacheCompression: true,
// 减少代码体积插件
plugins: ['@babel/plugin-transform-runtime'],
},
},
},
// 处理.css文件
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
// use: ['../loaders/style-loader', 'css-loader'],
},
// 处理.less文件
{
test: /\.less$/i,
use: ['style-loader', 'css-loader', 'less-loader'],
},
// 处理.sass或.scss文件
{
test: /\.(scss|sass)$/i,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
// 处理.styl文件
{
test: /\.styl$/i,
use: ['style-loader', 'css-loader', 'stylus-loader'],
},
// 处理图片资源,无需下载依赖,webpack内置
{
test: /\.(png|jpe?g|gif|webp)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb转为base64,默认小于8kb
},
},
// 修改输出目录
generator: {
filename: 'static/images/[name].[hash:8][ext]',
},
},
// 处理字体文件,不转base64
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
type: 'asset/resource',
generator: {
filename: 'static/fonts/[name].[hash:8][ext]',
},
},
// 处理md文件
{
test: /\.md$/i,
loader: path.resolve(__dirname, '../build/md-loader.js'),
// loader: 'html-loader',
},
// 处理svg文件
{
test: /\.svg$/i,
type: 'asset/resource',
generator: {
filename: 'static/images/[name].[hash:8][ext]',
},
}
],
},
],
},
// 插件
plugins: [
// 添加 eslint 规则,打包时有效
new ESLintPlugin({
context: path.resolve(__dirname, '../src'),
// 相对context的路径
exclude: ['./assets'],
}),
// 使用html插件自动引入打包文件
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html'),
}),
],
// 配置开发服务器
devServer: {
host: 'localhost',
port: 3000,
open: true,
// 热替换,默认开启且只对样式有效,js模块需要单独写代码(vue-loader等就是这样)
// hot: true,
// 配置代理
proxy: {
'/api': {
// 目标服务器
target: 'http://127.0.0.1:3000',
// 路径重写
pathRewrite: {
'^/api': '',
},
},
},
},
}
2、生产配置
const { Configuration } = require('webpack')
const path = require('path')
// 获取线程数
const os = require('os')
const threads = os.cpus()?.length ? os.cpus().length - 1 : 1
// 导入 eslint 插件
const ESLintPlugin = require('eslint-webpack-plugin')
// 导入 html 插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 导入抽离 css 样式插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// 导入压缩样式插件
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
// 导入压缩js插件
const TerserPlugin = require('terser-webpack-plugin')
// 导入preload插件
const PreloadWebpackPlugin = require('@vue/preload-webpack-plugin')
// 自定义打包文件分析插件
const AnalyzeWebpackPlugin = require('../plugins/analyze-plugins')
// 定义返回样式 loader 的方法
const getStyleLoaders = preProcessor => {
const loaders = [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: ['postcss-preset-env'],
},
},
},
]
if (preProcessor) {
loaders.push(preProcessor)
}
return loaders
}
// webpack5 基本配置
/**
* @type {Configuration}
*/
module.exports = {
// 入口
entry: './src/main.js',
// 输出
output: {
path: path.resolve(__dirname, '../dist'),
filename: 'static/js/[name].[hash:8].js',
// 清空上次打包内容
clean: true,
// 给打包的chunk文件命名
chunkFilename: 'static/js/[name].[hash:8].chunk.js',
// 给type: 'asset'静态资源命名
assetModuleFilename: 'static/media/[name].[hash:8][ext]',
},
// 生产模式
mode: 'production',
// 别名
resolve: {
alias: {
'@': path.resolve(__dirname, '../src'),
},
},
// 加载器
module: {
rules: [
{
// 每个文件只会匹配一种规则就结束
oneOf: [
// 处理.js文件,进行兼容性处理
{
test: /\.js$/,
exclude: /node_modules/,
use: [
// 使用thread-loader,开启多线程
{
loader: 'thread-loader',
options: {
workers: threads,
},
},
{
loader: 'babel-loader',
options: {
// 开启babel缓存
cacheDirectory: true,
// 关闭缓存文件压缩
cacheCompression: false,
// 减少代码体积插件,
plugins: ['@babel/plugin-transform-runtime'],
},
},
],
},
// 处理.css文件
{
test: /\.css$/i,
use: getStyleLoaders(),
},
// 处理.less文件
{
test: /\.less$/i,
use: getStyleLoaders('less-loader'),
},
// 处理.sass或.scss文件
{
test: /\.(scss|sass)$/i,
use: getStyleLoaders('sass-loader'),
},
// 处理.styl文件
{
test: /\.styl$/i,
use: getStyleLoaders('stylus-loader'),
},
// 处理图片资源,无需下载依赖,webpack内置
{
test: /\.(png|jpe?g|gif|webp)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb转为base64,默认小于8kb
},
},
},
// 处理字体文件,不转base64
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
type: 'asset/resource',
},
// 处理md文件
{
test: /\.md$/i,
loader: path.resolve(__dirname, '../build/md-loader.js'),
// loader: 'html-loader',
},
],
},
],
},
// 插件
plugins: [
// 添加 eslint 规则,打包时有效
new ESLintPlugin({
context: path.resolve(__dirname, '../src'),
// 相对context的路径
exclude: ['./assets'],
// 开启多线程
threads,
}),
// 使用html插件自动引入打包文件
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../public/index.html'),
}),
// 使用抽离样式插件
new MiniCssExtractPlugin({
filename: 'static/css/[name].[hash:8].css',
chunkFilename: 'static/css/[name].[hash:8].chunk.css',
}),
// 使用开启preload的插件
new PreloadWebpackPlugin({
rel: 'preload',
as: 'script',
}),
new AnalyzeWebpackPlugin(),
],
// 优化配置
optimization: {
minimizer: [
// 使用压缩样式插件
new CssMinimizerPlugin(),
// 使用压缩js插件
new TerserPlugin({
parallel: threads,
}),
],
splitChunks: {
chunks: 'all',
},
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`,
},
},
}
3、自定义loader
loader本质就是一个
module.exports
导出一个函数,这个函数的返回值是可以执行的js代码;
以下实现类似 style-loader 功能
module.exports = function () {}
module.exports.pitch = function (remainingRequest) {
// D:\前端\my-webpack\node_modules\css-loader\dist\cjs.js!D:\前端\my-webpack\src\assets\styles\index.css
const relativePath = remainingRequest
.split('!')
.map(absolutePath => {
return this.utils.contextify(this.context, absolutePath)
})
.join('!')
// ../../../node_modules/css-loader/dist/cjs.js!./index.css
return `
import style from '!!${relativePath}'
const styleEl = document.createElement('style');
styleEl.innerHTML = style;
document.head.appendChild(styleEl);
`
}
4、自定义插件
插件本质是一个导出一个类,内部有各种生命周期钩子;
以下实现打包后生成文件大小分析报告;
// 一个 JavaScript 类
class AnalyzeWebpackPlugin {
apply(compiler) {
// 在打包压缩后,资源输出前执行
compiler.hooks.emit.tap('AnalyzeWebpackPlugin', compilation => {
// 获取打包后资源的文件名和大小
const assets = Object.entries(compilation.assets)
let source = '# 打包文件大小分析 \n|名称|大小|\n|-|-|'
assets.forEach(([fileName, file]) => {
source += `\n|${fileName}|${Math.ceil(file.size() / 1024)}kb|`
})
// 输出分析报告
compilation.assets['analyze.md'] = {
source() {
return source
},
size() {
return source.length
},
}
})
}
}
module.exports = AnalyzeWebpackPlugin
5、补充 vite 插件
本质是一个函数,返回具有特定生命周期属性的对象;
一些官网链接
import fs from 'fs'
import path from 'path'
const viteAliasPlugin = ({ prefix = '@' } = {}) => {
console.log(prefix)
return {
name: 'vite-plugin-alias',
// 在解析 Vite 配置前调用
config: (config, env) => {
console.log('config')
// console.log(config)
// 获取src下文件夹名、文件名
const srcDir = fs.readdirSync(path.resolve(__dirname, '../../src'))
// 只留下文件夹名
const alias = srcDir.filter(item => {
return fs
.statSync(path.resolve(__dirname, '../../src', item))
.isDirectory()
})
console.log(alias) // ['api', 'assets', ...]
// 生成别名@
let aliasObj = {}
alias.forEach(item => {
aliasObj[`@/${item}`] = path.resolve(__dirname, `../../src/${item}`)
})
// {
// '@/api': 'D:\\前端\\React\\react-base\\src\\api',
// '@/assets': 'D:\\前端\\React\\react-base\\src\\assets',
// '@/layout': 'D:\\前端\\React\\react-base\\src\\layout',
// '@/pages': 'D:\\前端\\React\\react-base\\src\\pages',
// '@/router': 'D:\\前端\\React\\react-base\\src\\router',
// '@/store': 'D:\\前端\\React\\react-base\\src\\store',
// '@/utils': 'D:\\前端\\React\\react-base\\src\\utils',
// }
return {
// alias: {
// '@': path.resolve(__dirname, 'src'),
// },
resolve: {
alias: aliasObj,
},
}
},
// 在解析 Vite 配置后调用
configResolved: config => {
console.log('解析完成')
// console.log(config.resolve.alias)
},
}
}
export default viteAliasPlugin