webpack优化小结
- 生成runtime文件:runtimeChunk :true
- 多进程构建:parallel: true
- 源代码映射:devTool:'source-map'
- 懒加载(预加载):webpackChunkPrefetch、webpackChunkPreLoad
- 拆分包:splitChunks
- 热更新
- treeShaking(先标记,后删除)
- 资源压缩:compress
- 小文件转为DataUrl(base64)方式,减少请求次数
webpack打包时间和内存分析
打包时间
插件 SpeedMeasurePlugin
https://www.npmjs.com/package/speed-measure-webpack-plugin
内存分析
插件webpack-bundle-analyzer
https://v4.webpack.docschina.org/guides/code-splitting/#bundle-%E5%88%86%E6%9E%90-bundle-analysis-
https://www.npmjs.com/package/webpack-bundle-analyzer
const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
const {VueLoaderPlugin} = require('vue-loader')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')
const { DefinePlugin } = require('webpack')
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const PurgeCSSPlugin = require('purgecss-webpack-plugin')
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const CompressionPlugin = require("compression-webpack-plugin");
var InlineChunkHtmlPlugin = require('inline-chunk-html-plugin');
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const webpack = require('webpack')
const smp = new SpeedMeasurePlugin();
module.exports = smp.wrap({
mode:'development',
entry:{
//多入口
main:'./src/main.js',
index:'./src/index.js'
},
devTool:'source-map',
output:{
filename:'js/[name].bundle.js',
path:path.resolve(__dirname,'build'),
chunkFilename:'js/chunk_[name].js',//这里的name受optimization里的chunkIds影响,或者使用魔法注释可以重新命名import(/*webpackChunkName:'title'*/'./title')
//publicPath:'https://abc.cnd.com',//给打包好的js前面加cdn地址
//assetModuleFilename:"img/[name].[hash:6][ext]"//asset配置
},
/**
* 第三方扩展设置,防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖
* https://v4.webpack.docschina.org/configuration/externals/#externals
*/
externals:{
lodash:'-',//排除lodash,不要打包,需要在script里加lodash的cnd地址,优点:可以及时更新第三方代码
},
/**
* 配置模块解析规则
* https://webpack.docschina.org/configuration/resolve/#root
*/
resolve:{
extensions:[".js",".json",".ts",".jsx",".vue"],//按顺序解析这些后缀名,没有写后缀时默认补全
alias:{
'@':path.resolve(__dirname,'src')//@代表的目录,在文件里可以用'@components/list'
},
// mainFields: ['browser', 'module', 'main']
//在我们 import * as Upstream from 'upstream' 时,这实际上会从包的package.json里的 browser 属性解析文件
},
optimization:{
/**
* https://webpack.docschina.org/guides/code-splitting/#splitchunksplugin
* webpackChunkPrefetch:true // 浏览器闲置时/加载完成后 加载
* webpackChunkPreLoad:true // 立即(并行)加载
*/
usedExports:true,//标记没用到的函数,结合TerserPlugin来删除没有的代码
minimize:true,//告知 webpack 使用 TerserPlugin 或其它在 optimization.minimizer定义的插件压缩 bundle
minimizer:[
/**
* 对js代码进行压缩的工具(将变量变成a\b\o)
* https://webpack.docschina.org/plugins/terser-webpack-plugin/
* 参数:https://github.com/terser/terser
*/
new TerserPlugin({
cache: true,//启用文件缓存
parallel: true,//使用多进程构建
sourceMap: true, //启用源代码映射
terserOptions: {
// https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions
},
extractComments:false,//省略中间产生的注释文件
}),
],
runtimeChunk :true,//做长期缓存,生成一个runtime文件
chunkIds:'natural',//natural-文件按自然数命名&排序(影响缓存),named-按名称命名,deterministic-根据文件内容生成hash命名,
splitChunks:{//把动态导入(import)的包拆出来,
chunks:'initial',//all,async 对同步导入生效还是异步,还是全部
minSize:20000,//拆完包后的最小体积,小于这个体积就不拆
maxSize:20000,//体积大于则个值则进行拆分
minChunks:1,//至少引用几次则进行拆分
cacheGroups:{//拆包的过程中进行分组
vendors:{
test:/[\\/]node_modules[\\/]/,
filename:'js/[id]_vender.js',//把第三方库都放在vender.js里
priority:-10,//优先级
}
}
}
},
devServer:{
port:8000,
hot:true,//热更新
hotOnly:true,
open:false,
compress:true,//基础压缩,gzip
proxy:{
'/api':{//标记
target:'https://api.github.com/',//
pathRewrite:{"^/api":""},//对api做重写
changeOrigin:true,//修改请求头里的host字段
}
}
},
plugins:[
new CleanWebpackPlugin(),//自动删除打包后目录下的文件
new htmlWebpackPlugin({//该插件为你生成一个 HTML 文件
// filename:'build.html'
title:'myAppTitle',
template:'./public/index.html'//使用自己的html模板
}),//默认生成index.html文件并引入打包后的js文件
new VueLoaderPlugin(),
new DefinePlugin({//在 编译时 将代码中的变量替换为其他值或表达式
BASE_URL:'"./"'
}),
new CopyWebpackPlugin({
patterns:[
{
from:'public',
//to: 省略to就会使用output里的path
globOptions:{
ignore:['**/index.html']//省略的文件,**/表示从from下开始查找
}
}
]
}),
/**
* 将css代码提取到单独的文件中
* https://webpack.docschina.org/plugins/mini-css-extract-plugin/
*/
new MiniCssExtractPlugin({
filename:'css/[name].[hash:8].css'
}),
/**
* 对css文件进行压缩
* https://webpack.docschina.org/plugins/css-minimizer-webpack-plugin/
*/
new CssMinimizerPlugin(),
new webpack.optimize.ModuleConcatenationPlugin(),//scope hoisting 仅适用于es module,对模块有优化功能
/**
* css文件的tree shaking配置
* https://www.npmjs.com/package/purgecss-webpack-plugin
* 注意:注释掉的代码的样式不会被删除
*/
new PurgeCSSPlugin({
//需要告诉他对哪些css进行处理
paths: glob.sync(`${path.resolve(__dirname,'./src')}/**/*`, { nodir: true }),
safelist:function(){//安全列表
return {
standard:['body','html']//树摇的时候不会把这两个样式摇掉
}
}
}),
new CompressionPlugin({
test:/\.(js)$/,
minRation:0.2,//压缩比例
threshold:0,//体积大于多少就压缩
algorithm:'gzip',//压缩算法
}),//压缩资源,test.js->test.js.gz
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime.*\.js/]),//将运行时文件注入到html文件中,优点:不用单独请求runtime文件
],
module:{
rules:[
{
test:/\.css$/,
use:[MiniCssExtractPlugin.loader, "css-loader"],
//use:['style-loader','css-loader'] 要抽离css文件用MiniCssExtractPlugin.loader代替style-loader
sideEffects:true,
},
{
test:/\.less$/,
use:['style-loader',
{
loader:'css-loader',
options:{
importLoaders:2//在工作中可以往前找一个loader
}
},
'postcss-loader',
'less-loader'//从上往下,从右往左
]
},
{
test:/\.(jpe?g|png|gif|svg)$/,
type:'asset',
generator:{
filename:'img/[name].[hash:6][ext]'
}
},
{
test:/\.js$/,
use:[{
loader:'babel-loader',
options:{
presets:[
'@babel/preset-env'
]
}
}]
},
{
test:/\.(woff2?|ttf)$/,
type:'asset/resource',
generator:{
filename:'font/[name].[hash:6][ext]'
}
},
{
test:/\.jsx$/,
use:['babel-loader']
},
{
test:/\.ts$/,
use:['babel-loader']
},
{
test:/\.vue$/,
use:['vue-loader']
}
]
}
})
/**
postcss-preset-env 转换css的功能的集合
browerlistsrc 对需要加前缀的代码进行处理,需要在browerlistsrc设置规则,服务于js、css的兼容的筛选条件
*/