最近做公司项目,用webpack 打包发现输出的bundle.js文件体积超大,9M多。查阅相关博客,最后处理得到的bundle.js压缩到202k。由于目前用的webpack是4.10.2, 在处理过程中,也遇到了关于webpack版本的一些坑。根据webpack version 4+, 整理优化思路如下:
1. 去除devtool(罪魁祸首)
webpack.base.config.js :
const path = require('path');
const autoprefixer = require('autoprefixer');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: ''
},
resolve: {
extensions: ['.js']
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
.......
]
},
plugins: [
new HtmlWebpackPlugin({
template: __dirname + '/src/index.html',
filename: 'index.html',
inject: 'body'
}),
new CopyWebpackPlugin([{
from: __dirname + '/src/assets/js',
to: __dirname + '/dist/js'
}])
]
};
webpack.dev.config.js :
const path = require("path");
const merge = require("webpack-merge");
const webpackConfigBase = require("./webpack.base.config");
const webpackConfigDev = {
devtool: 'cheap-module-eval-source-map',
mode:'development',
plugins:[
new BundleAnalyzerPlugin()
],
devServer:{
contentBase: path.join(__dirname,"../public"),
hot: true,
host:'0.0.0.0',
inline: true,
port: 8080,
}
}
module.exports = merge(webpackConfigBase, webpackConfigDev);
webpack.prod.config.js :
const path = require("path");
const webpackConfigBase = require("./webpack.base.config");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const merge = require("webpack-merge");
const webpackConfigProd = {
mode: "production",
plugins:[
new CleanWebpackPlugin(["build"],{
root: path.join(__dirname,"../")
})
]
};
module.exports = merge(webpackConfigBase, webpackConfigProd);
在package.json中修改scripts:
"scripts": {
"start": "webpack-dev-server --mode development --config ./webpack.dev.config.js",
"build": "rimraf dist && webpack --mode production --config ./webpack.prod.config.js"
}
2. 代码压缩
这边遇到一个坑,查阅相关博客,看到都会有这样一段代码:
plugins:[
...
new webpack.optimize.UglifyJsPlugin({
compress:{
warnings:false
}
})
...
]
webpack.optimize.UglifyJsPlugin是webpack自带的压缩工具,不需要import,但是在运行时确报了如下错误:
webpack.optimize.UglifyJsPlugin has been removed, please use config.optimization.minimize instead.
查阅相关资料后发现,笔者使用的webpack版本时4.10.2,而在webpack4.0版本中已经废弃了之前 UglifyJsPlugin的用法。若要使用此功能,需要使用uglifyjs-webpack-plugin插件。
步骤如下:
const UglifyJsPlugin=require('uglifyjs-webpack-plugin');
...
//压缩js
optimization: {
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
compress: false
}
})
]
}
...
注意:删除以前的写法,将optimization的JS压缩与plugins并排写在一起,而不是像以前一样写在plugins里面。
...
"build": "webpack -p"
...
这是一个自动化的方法来构建生产环境下的项目,自动使用UglifyJsPlugin进行最小化 ,还会运行使用LoaderOptionsPlugin。
等价于:webpack --optimize-minimize --define process.env.NODE_ENV="'production'"
关于webpack-p的具体用法,没有做具体研究,笔者使用webpack -p后bundle.js减少了5KB。
参考资料:解决webpack打包文件过大的问题
3. 分离CSS
安装插件:
npm install extract-text-webpack-plugin --save-dev
在webpack.config.js中添加如下代码:
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
...
module: {
rules: [
...
{
test: /\.css$/,
exclude: /node_modules/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: [{
loader: "css-loader",
options: {
importLoaders: 1,
modules: true,
localIdentName: '[name]__[local]__[hash:base64:5]'
}
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
autoprefixer({
browsers: [
"> 1%",
"last 2 versions"
]
})
]
}
}],
publicPath: "../"
})
}
]
},
plugins: [
new ExtractTextPlugin("bundle.css"),
...
]
...
};
然后运行打包程序,也遇到了一个版本问题造成的坑,在打包运行时出现如下错误:
Error: Chunk.entrypoints: Use Chunks.groupsIterable and filter by instanceof Entrypoint instead
查阅相关资料发现,extract-text-webpack-plugin还不能支持webpack4.0.0以上的版本。
解决办法:
npm install --save-dev extract-text-webpack-plugin@next
会下载到+ [email protected]
,然后就可以正常打包了。
通过分离CSS,从原bundle.js中分离出了15KB的bundle.css。
参考文章:webpack使用extract-text-webpack-plugin打包时提示错误Use Chunks.groupsIterable and filter by instanceof Entryp
4. 使用CDN引入资源
通过以上3种操作,bundle.js已经减少到1190KB,还是远超与limitation的224KB。通过可视化工具webpack-bundle-analyzer分析发现,bundle.js之所以这么大,主要是由于打包时加入了echarts、moment和jQuery,所以考虑把他们提取出来,改用cdn版本的echarts、moment和jQuery。
bootcdn链接:https://www.bootcdn.cn/
以echarts为例,操作步骤如下:
...
externals: {
"echarts": "echarts"
}
...
externals中的key是给import的时候用的,value表示的是如何在global中访问到该对象,这里是window.echarts;
echarts和moment都使用了CDN资源后,bundle.js减少到了202KB,基本达到了低于244KB的要求,但是相对还是很大,后面还可以进行其他的相关处理,比如:提取第三方库...参考文章:webpack打包优化解决方案
5. 提取第三方库
new webpack.optimize.CommonsChunkPlugin({
filename:"common.js",
name:"commons"
});
待补充
6. DLL方式
待补充
7.使用compresion-webpack-plugin插件将静态资源压缩,并生成.gz文件。
在webpack.prod.config.js中加入如下代码:
const CompressionPlugin = require('compression-webpack-plugin');
plugins: [
...
new CompressionPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: /\.js$|\.css$|\.html$/
})
...
]
但是关于是否使用gzip,目前还存在争议,如果bundle.js并不是太大,建议无需进行gzip压缩。
相关参考文章:
1. webpack打包优化并开启gzip
2. 要不要用gzip优化前端项目
最后,在这里,安利一下我使用的可视化分析bundle.js 资源的webpack插件,webpack-bundle-analyzer,可以清晰的看到项目中一共有多少个包,包的体积是多少,里面加载了哪些东西,大小是多少。
作用 : 将bundle.js中捆绑的资源大小及关系,用交互式可缩放树形图直观的表示出来,从而有助于知道各资源的大小及关系,并进行优化。
效果图如下:
另外,webpack-bundle-analyzer还支持缩小捆绑,并支持gzipped显示。详细功能可查看官网
webpack-bundle-analyzer安装和使用
npm install --save-dev webpack-bundle-analyzer
在webpack.config.js中:
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [new BundleAnalyzerPlugin()]
}
BundleAnalyzerPlugin构造函数中可配置的对象:
new BundleAnalyzerPlugin({
// 可以是`server`,`static`或`disabled`。
// 在`server`模式下,分析器将启动HTTP服务器来显示软件包报告。
// 在“静态”模式下,会生成带有报告的单个HTML文件。
// 在`disabled`模式下,你可以使用这个插件来将`generateStatsFile`设置为`true`来生成Webpack Stats JSON文件。
analyzerMode: 'server',
// 将在“服务器”模式下使用的主机启动HTTP服务器。
analyzerHost: '127.0.0.1',
// 将在“服务器”模式下使用的端口启动HTTP服务器。
analyzerPort: 8888,
// 路径捆绑,将在`static`模式下生成的报告文件。
// 相对于捆绑输出目录。
reportFilename: 'report.html',
// 模块大小默认显示在报告中。
// 应该是`stat`,`parsed`或者`gzip`中的一个。
// 有关更多信息,请参见“定义”一节。
defaultSizes: 'parsed',
// 在默认浏览器中自动打开报告
openAnalyzer: true,
// 如果为true,则Webpack Stats JSON文件将在bundle输出目录中生成
generateStatsFile: false,
// 如果`generateStatsFile`为`true`,将会生成Webpack Stats JSON文件的名字。
// 相对于捆绑输出目录。
statsFilename: 'stats.json',
// stats.toJson()方法的选项。
// 例如,您可以使用`source:false`选项排除统计文件中模块的来源。
// 在这里查看更多选项:https: //github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21
statsOptions: null,
logLevel: 'info' 日志级别。可以是'信息','警告','错误'或'沉默'。
})
参考文章: