背景
最近自己整了一个基于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进来了了,其实我永不到那么多,分别是
//修改前 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 - 1parallel: true,// Enable file cachingcache: 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},}}} })
感谢!