// 导出webpack 配置
// vue inspect --mode=development > webpack.config.dev.js
// vue inspect --mode=production > webpack.config.prod.js
/**
* 通用环境提升构建速度
*
* 1. webpack nodejs 更新到最新版本
* 2. 将loader 应用于最少数量的必要模块
* 3. 尽量少的使用 loader / plugin, 每个额外的都有其启动时间
* 4. 移除不必要的模块
* 5. 多进程、多实例 TerserPlugin.parallel
* 6. 减少 resolve.modules, resolve.extensions, resolve.mainFiles,
* resolve.descriptionFiles 中条目数量,提高解析速度
* 7. babel-loader、TerserPlugin.cache、hard-source-webpack-plugin 开启缓存
* 8. 图片压缩
* 9. 动态 Polyfill 服务,不同设备浏览器返回不同的 Polyfill
* https://polyfill.io/v3/polyfill.min.js
* */
/**
* 代码分离 3-3 动态导入 import('./math.js').then()
*
* 懒加载 事件监听后 import
*
* 预获取 Prefetch 将来可能用到的资源 ,会在页面头部生成
* <link rel="prefetch" href="math.js" >,指示浏览器在空闲时间加载math.js
*
* 预加载 Preload 当前可能会用到的资源 和懒加载效果类似
**/
// 魔法注释
// import(/* webpackChunkName: math, webpackPreload: true, webpackPrefetch: true */ './math.js').then()
// npm install html-webpack-plugin -D
// 生成一个 HTML5 文件, 在 body 中使用 script 标签引入你所有 webpack 生成的 bundle
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack')
// npm install mini-css-extract-plugin -D
// 将 CSS 提取到单独的文件中, webpack5可用
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// npm install css-minimizer-webpack-plugin -D
// 使用 cssnano 优化和压缩 CSS
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
// npm install terser-webpack-plugin -D
// 压缩 JavaScript
const TerserPlugin = require("terser-webpack-plugin");
// hard-source-webpack-plugin 图片压缩
// npm install --save-dev speed-measure-webpack-plugin
// 打包速度分析
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
// npm install webpack-bundle-analyzer -D
// 打包结果分析工具。
// vue.config.js 中,不用添加插件,在命令行后添加 --report 可用
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
// npm install workbox-webpack-plugin -D
// 渐进式网络应用程序(progressive web application - PWA) serviceWorker 服务停了 离线时 浏览器页面还能继续运行
const WorkboxPlugin = require('workbox-webpack-plugin');
// npm install @babel/polyfill -D
/**
* npm install webpack-merge -D 合并配置文件
*
* const { merge } = require('webpack-merge')
const commonConfig = require('./webpack.config.common')
const prodConfig = require('./webpack.config.prod')
const devConfig = require('./webpack.config.dev')
module.exports = (env) => {
switch (true) {
case env.development:
return merge(commonConfig, devConfig)
case env.production:
return merge(commonConfig, prodConfig)
default:
return new Error()
}
}
*
* */
const path = require('path');
module.exports = smp.wrap({
entry: 'index.js',
// entry: {
// main: {
// import: ['./src/app.js', './src/app2.js'],
// dependOn: 'lodash', // 重复公共包
// filename: 'channel1/[name].js'
// },
// main2: {
// import: './src/app3.js',
// dependOn: 'lodash', // 重复公共包
// filename: 'channel2/[name].js'
// },
// lodash: { // 代码分离 3-1 重复公共包在此
// import: 'lodash',
// filename: 'common/[name].js'
// }
// },
output: {
// webpack 配置文件同层的 dist 文件夹
path: path.resolve(__dirname, './dist'),
filename: 'scripts/[name].[contenthash].js', // 生成的js 文件, name 是entry下的键名
clean: true, // 打包之前自动清理dist下的文件
assetModuleFilename: 'images/[contenthash][ext]',
publicPath: 'http://localhost:8080/'
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src') // 模块加载时可用@替代src
},
extensions: ['.js', '.json', '.vue'] // 模块加载时 不写文件扩展名 同名 加载顺序
},
mode: 'development', // 'none' | 'development' | 'production'
// 开发环境调试代码可看到出错的具体位置
devtool: 'cheap-module-source-map',
// npm install webpack-dev-server -D
// 简写: dev-server
devServer: {
static: path.resolve(__dirname, './dist'), // 监听目录
compress: true, // 开启压缩代码 gzip
port: 8080,
host: '0.0.0.0',
// https: true, // 模拟https环境测试
http2: true,
historyApiFallback: true,
hot: true, // 模块热替换
liveReload: true, // 模块热加载
},
plugins: [
new HtmlWebpackPlugin({
title: '页面标题',
template: './index.html', // 依据这个文件生成dist文件价下的index.html
filename: 'app.html', // 生成的html 文件
inject: 'body', // script 标签插入到body中
chunks: ['main', 'main2', 'lodash'] // 默认entry下的全部
}),
new webpack.ProvidePlugin({
_: 'lodash' // 全局预置依赖 业务代码里不用引入lodash,也可暴露在全局
}),
new MiniCssExtractPlugin({
filename: 'styles/[contenthash].css' // 提取单独的css文件
}),
new BundleAnalyzerPlugin(),
new WorkboxPlugin.GenerateSW({
clientsClaim: true, // 快速启用 ServiceWorkers
skipWaiting: true // 跳过等待 不允许遗留任何“旧的” ServiceWorkers
})
/** 代码里注册才能用 serviceWorker
* if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js').then(registration => {
console.log('注册 Service Worker ok: ', registration);
}).catch(registrationError => {
console.log('注册 Service Worker failed: ', registrationError);
});
});
}
*/
],
module: {
rules: [
/*
* 资源类型有4种:
* resource资源
* inline资源
* source资源
* 通用资源
*/
{
test: /\.png$/,
type: 'asset/resource', // resource资源 会导出单独图片文件
generator: {
filename: 'images/[contenthash][ext]' // 优先级比 assetModuleFilename 高
}
},
{
test: /\.svg$/,
type: 'asset/inline' // inline资源 会导出base64
},
{
test: /\.txt$/,
type: 'asset/source' // source资源 会导出源代码
},
{
test: /\.jpg$/,
type: 'asset', // 通用资源 自动选择, parser 未设置时,小于8k用 'asset/inline',大于8k用 'asset/resource'
parser: {
dataUrlCondition: {
maxSize: 4 * 1024 * 1024 // 4M
}
}
},
// npm install style-loader css-loader less-loader -D
// npm install postcss-loader postcss autoprefixer -D
{
test: /\.(css|less)$/,
// /use: [MiniCssExtractPlugin.loader /* css抽出单独文件, 从后向前执行loader */, 'css-loader', 'less-loader']
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true // 开启css模块 避免类名重复
}
},
'less-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'postcss-preset-env', // postcss-preset-env 包含 autoprefixer,因此如果你已经使用了 preset 就无需单独添加它了
// 'autoprefixer',
{
// 其他选项
},
],
],
},
},
}
] // 样式在htmlstyle标签中, 从后向前执行loader
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
type: 'asset/resource'
},
// npm install csv-loader xml-loader -D
// 加载 csv xml 文件数据
{
test: /\.(csv|tsv)$/,
use: ['csv-loader']
},
{
test: /\.xml$/,
use: ['xml-loader']
},
// npm install toml yaml json5 -D
// 加载 toml yaml json5 文件数据
{
test: /\.toml$/,
type: 'json',
paeser: {
parse: toml.parse
}
},
{
test: /\.yaml$/,
type: 'json',
paeser: {
parse: yaml.parse
}
},
{
test: /\.json5$/,
type: 'json',
paeser: {
parse: json5.parse
}
},
// npm install babel-loader @babel/core @babel/preset-env @babel/runtime @babel/plugin-transform-runtime core-js@3 eslint-loader -D
// babel-loader, es6 转为 es5
// @babel/core, babel核心
// @babel/preset-env, babel插件合集
// @babel/runtime, 可兼容 async await 写法
// @babel/plugin-transform-runtime,
{
test: /.\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
{
targets: [
'last 1 version',
'> 1%'
],
useBuiltIns: 'usage',
corejs: 3
}
],
plugins: [
[
'@babel/plugin-transform-runtime'
]
]
}
},
'eslint-loader'
],
},
// npm i imports-loader -D 细粒度预置依赖
{
test: require.resolve('./src/index.js'),
use: 'imports-loader?wrapper=window'
},
// npm i exports-loader -D 全局导出
{
test: require.resolve('./src/global.js'),
use: 'exports-loader?type=commonjs&exports=file'
}
]
},
externalsType: 'script', // 第三方库 使用的标签
externals: { // 从输出的 bundle 中排除下列依赖
jquery: [ // 第三方库
'https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js',
'$'
]
},
// 优化配置 mode 为 production 可用
optimization: {
minimizer: [
new CssMinimizerPlugin(),
new TerserPlugin({
parallel: 4 // 使用多进程并发运行以提高构建速度
})
],
splitChunks: {
// chunks: 'all' // 代码分离 3-2 自动抽离公共代码
cacheGroups: { // 缓存组 存放 第三方文件 一般在node_modules
vendor: {
test: /[\\/]node_modules[\\/]/, // 当 webpack 处理文件路径时,它们始终包含 Unix 系统中的 / 和 Windows 系统中的 \。这就是为什么在 {cacheGroup}.test 字段中使用 [\\/] 来表示路径分隔符的原因。
name: "vendors",
chunks: 'all',
minSize: 20480, // 拆分包的大小(以 bytes 为单位), 至少为minSize,如果包的大小不超过minSize,这个包不会拆分
maxSize: 40960 // 将大于maxSize的包,拆分为不小于minSize的包
}
}
},
usedExports: true // tree-shaking
},
performance: {
hints: false // 关闭性能提示
},
cache: {
type: 'filesystem'
}
});