背景
最近自己整了一个基于webpack4和react开发的博客demo项目,一路整下来磕磕碰碰但也实现了功能,就准备发到阿里云上面去看看,借用了同事的阿里云小水管服务器,配置完成之后首页加载花了十几秒,打开控制台network查看资源,打包的js体积有将近6M,及其影响访问体验,于是就开始了优化的路。
原因和解决方法
在webpack的配置文件中,对公共js做了抽取,分别会打包出react-verdor.js和antd-verdor.js
,优化前的antd-verdor足足有4m大小,估计是把antd组件全部加载下来了,顺着这个思路查找解决方案。
optimization.splitChunks: {
chunks: 'all',
cacheGroups: {
"react-vendor": {
test: (module) => (/react/.test(module.context) || /redux/.test(module.context)
|| /classnames/.test(module.context) || /prop-types/.test(module.context)),
priority: 3,
reuseExistingChunk: false
},
"antd-vendor": {
// || /[\\/]node_modules[\\/]/.test(module.context)
test: (module) => (/antd/.test(module.context)),
priority: 2,
reuseExistingChunk: false
},
}
}
antd官方推荐按需加载,我开始也是根据官方推荐做的,在.babelrc做如下配置
{
"plugins": [
["import", {
"libraryName": "antd",
"libraryDirectory": "es",
"style": "css"
}]
]
}
不过事实是打包文件有4M,那肯定是配置错了,继续查找别的配置方法,终于找到一个管用的,在webpack配置文件中做如下配置(本质上还是自己太菜,不理解.babelrc)
{
test: /\.(js|jsx)/,
use: {
loader:'babel-loader',
options: {
presets: ["env", "react", 'stage-0'],
plugins: [
['import', [{ libraryName: 'antd', style: true }]]
]
}
},
include: resolve('src')
},
配置完成后进行打包,体积明显变小了,antd-vendor.js的大小变成200多K。antd按需引入的问题解决后,看别的文件还是挺大的,于是决定把REACT中的组件也做成按需加载的方式,拆分更多的js出来,这里使用了react-loadable实现,拆分完成之后继续打包,发现部分子js的体积明显过大
,继续查找原因。在浏览器中跳转到对应的页面查看加载的js,查看js文件中的注释
,发现有两个第三方插件被我全部import进来了了,其实我永不到那么多,分别是
crypto和highlightjs,crypto是一个加密算法库,提供md5和sha 256等经典加密算法,我这个项目中只在登录时用到了一次加密密码,而那个页面对应的js有500多k,问题显而易见,通过下面这个修改把问题解决了
//修改前
import crypto from ('crypto')
module.exports = {
MD5_SUFFIX: 'sskjtxdywdddzyjknn',
md5: function (pwd) {
let md5 = crypto.createHash('md5');
return md5.update(pwd).digest('hex')
},
}
//修改后
const md5 = require("crypto-js/md5")
module.exports = {
MD5_SUFFIX: 'sskjtxdywdddzyjknn',
md5: function (pwd) {
return md5.update(pwd).digest('hex')
},
}
解决引入highlightjs后文件的过大的的思路和上面类似,我选择了直接加载hightlightjs保留核心功能后的压缩代码,size只有30k,而之前的默认import后的size有将近1M
还能优化吗
前面发现的问题不外乎是由第三方库造成的,解决了前面的问题后,此时的js体积已经从之前的6M降到1.8M了。那么还能继续通过webpack配置来优化打包后的文件大小吗?抱着这个疑问继续查找解决方案,最后决定试一试这个文章说明的方法
https://zhuanlan.zhihu.com/p/36280323
一顿操作下来打包出来的文件只减小了30K,最后再放上配置文件js的完整代码,希望能有大佬提出建议,非常
//webpack.base.config.js
module.exports = {
/*entry: {
app: './src/index.jsx',
},*/
output: {
publicPath: process.env.NODE_ENV === config.prod.ENV
? config.prod.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.jsx', '.json'],
alias: {
'@components': path.resolve(__dirname, '../src/components'),
'@': resolve('src'),
}
},
module: {
rules: [
{
test: /\.(js|jsx)/,
use: {
loader: 'babel-loader',
options: {
presets: ["env", "react", 'stage-0'],
plugins: [
['import', [{ libraryName: 'antd', style: true }]]
]
}
},
include: resolve('src')
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: assetsPath('img/[name].[ext]')
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: assetsPath('media/[name].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: assetsPath('font/[name].[ext]')
}
},
{
test: /\.css$/,
use: [{
loader: 'style-loader'
}, {
loader: "css-loader",
options: {
name: "[path][name].[ext]",
}
}]
},
{
test: /\.less$/,
exclude: /node_modules/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
]
},
{
test: /\.less$/,
include: /node_modules/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
'loader': 'less-loader',
options: {
javascriptEnabled: true
}
}
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css'
}),
new LodashModuleReplacementPlugin
]
}
//webpack.prod.config.js
const webpackConfig = merge(baseWebpackConfig, {
entry: {
app: './src/index.jsx',
},
mode: 'production',
devtool: false,
output: {
path: config.prod.assetsRoot,
filename: assetsPath('js/[name].[chunkhash].js'),
chunkFilename: assetsPath('js/[name].[chunkhash].js'),
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
title: pkg.description,
template: path.resolve(__dirname, '../index.html'),
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../src/static'),
to: config.prod.assetsSubDirectory,
}
])
],
optimization: {
minimizer: [
new UglifyJsPlugin(),
new OptimizeCSSAssetsPlugin(),
new TerserPlugin({
terserOptions: {
parse: {
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
},
mangle: {
safari10: true,
},
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
// Use multi-process parallel running to improve the build speed
// Default number of concurrent runs: os.cpus().length - 1
parallel: true,
// Enable file caching
cache: true,
sourceMap: false,
}),
],
splitChunks: {
chunks: 'all',
cacheGroups: {
"react-vendor": {
test: (module) => (/react/.test(module.context) || /redux/.test(module.context)
|| /classnames/.test(module.context) || /prop-types/.test(module.context)),
priority: 3,
reuseExistingChunk: false
},
"antd-vendor": {
// || /[\\/]node_modules[\\/]/.test(module.context)
test: (module) => (/antd/.test(module.context)),
priority: 2,
reuseExistingChunk: false
},
}
}
}
})
感谢!