前言:
在前端开发的过程中,webpack的使用基本已经成为了标配。如何配置webpack,可以参考官网:www.webpackjs.com/concepts/。
我相信,在前端开发过程中,你一定遇到过这样苦恼的事情,比如:1. 在开发过程中,对项目的某个页面进行小小的改动甚至只是修改一个国际化的内容,项目的编译时间都超过了让你难以接受的范围;2. 由于在开发模式下,可能会出现频繁的打包替换到生产环境进行自验证的问题,这个时候,你执行npm run build之后,webpack运行了长达10分钟之久,这个时候,我们的心态也许就崩了。既然如此,那为何不自己动手优化一下webpack的打包编译时间呢?
开始优化:
1. webpack当前配置
环境版本
|————webpack: 4.12.0
|————webpack-cli:3.0.8
|————node:v12.18.3
项目中的webpack配置如下:
const path = require('path');const VueLoaderPlugin = require('vue-loader/lib/plugin');const MiniCssExtractPlugin = require('mini-css-extract-plugin');const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');const webpack = require('webpack');const NODE_ENV = process.env.NODE_ENV || 'development';const isProd = NODE_ENV === 'production';const isQuickMode = process.env.QUICK_MODE === 'true';const isAnalysis = process.env.ANALYZE_BUNDLE === 'true';function resolve(dir) { return path.join(__dirname, '..', dir);}const cssLoader = [ { loader: 'css-loader', options: { minimize: isProd }, }, 'postcss-loader', 'sass-loader',];const webpackCommonPlugins = [ new VueLoaderPlugin(), new MiniCssExtractPlugin({ filename: 'common.[contenthash].css', }), ...(isProd ? [] : [new FriendlyErrorsPlugin()]), new HardSourceWebpackPlugin(), // new webpack.optimize.ModuleConcatenationPlugin(), new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"production"', }), new webpack.HotModuleReplacementPlugin(),];if (isQuickMode) { webpackCommonPlugins.unshift( new ForkTsCheckerWebpackPlugin({ memoryLimit: 1024 * 4, workers: ForkTsCheckerWebpackPlugin.ONE_CPU_FREE, ignoreDiagnostics: [2307, 1323], }), );}if (isAnalysis) { webpackCommonPlugins.push( new BundleAnalyzerPlugin({ async: false, }), );}module.exports = { mode: NODE_ENV, devtool: isProd ? false : '#cheap-module-source-map', output: { path: resolve('dist'), publicPath: '/dist/', library: 'PARTNER_ADMIN', jsonpFunction: 'PARTNER_ADMINJsonp', filename: '[name].[hash].js', globalObject: 'this', }, externals: { jquery: 'jQuery', }, resolve: { alias: { public: resolve('public'), vue$: 'vue/dist/vue.esm.js', '@': resolve('src'), }, extensions: ['.webpack.js', '.web.js', '.ts', '.js', '.json', '.vue'], }, module: { noParse: /es6-promise\.js$/, rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { compilerOptions: { preserveWhitespace: false, }, }, }, { enforce: 'pre', test: /\.js$/, include: [resolve('src')], exclude: [resolve('node_modules')], loader: 'eslint-loader', }, { test: /\.js$/, loader: 'babel-loader?cacheDirectory=true', include: [resolve('src')], }, { test: /\.ts$/, loader: 'ts-loader', options: { appendTsSuffixTo: [/\.vue$/], happyPackMode: isQuickMode, }, }, { test: /\.(png|jpg|gif|svg|woff|ttf)$/, loader: 'url-loader', options: { limit: 20000, name: '[name].[ext]?[hash]', }, }, { test: /\.s(c|a)ss$/, use: [ 'vue-style-loader', 'css-loader', { loader: 'sass-loader', // Requires sass-loader@^8.0.0 options: { implementation: require('sass'), sassOptions: { fiber: require('fibers'), indentedSyntax: true, // optional }, }, }, ], }, { test: /\.(sc|c)ss$/, oneOf: [ { test: /App/, resourceQuery: /\?vue/, use: [MiniCssExtractPlugin.loader, ...cssLoader], }, { use: ['vue-style-loader', ...cssLoader], }, ], }, ], }, performance: { maxEntrypointSize: 300000, hints: isProd ? 'warning' : false, }, plugins: webpackCommonPlugins, devServer: { contentBase: resolve('dist'), hot: true, open: true, },};
这个时候,运行npm run build之后,构建的时间为11.84s (PS: 这个时间会因为每个人的电脑配置以及实际的项目大小而变化)。为了准确分析,每次修改都以三次构建的时间为准。
构建时间记录(3次):11.84s, 10.44s, 11.50s
2. 使用插件:hard-source-webpack-plugin
作用
该插件的作用是为打包后的模块提供缓存,且缓存到本地硬盘上。默认的缓存路径是:mode_modules/.cache/hard-source
。
如何使用
插件的使用是比较简单的。
(1)首先安装插件
npm i -D hard-source-webpack-plugin 或者 yarn add -d hard-source-webpack-plugin
(2)导入并使用:在webpack的配置文件中,
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const webpackCommonPlugins = [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({
filename: 'common.[chunkhash].css',}),
... isProd ? [] : [ new FriendlyErrorsPlugin() ],
new HardSourceWebpackPlugin(), // 加在此处;
打包时间
当第一次build时,时间不会有太大变化,但从第二次开始,构建时间大概可以节约70%-80%的样子(这是网上流传的说法,不过实际上跟你本身配置也有一定关系)。这个插件也是我在实际使用的时候效果最明显的一个。下面记录从第二次开始的构建时间。
构建时间记录(3次):6.97s, 7.43s, 7.18s
通过上述三次时间的对比,可以看出大概优化了28%—40%左右的样子。
3. 使用DefinePlugin
插件说明及使用
- 该插件将“production”替换到process.env.NODE_ENV;
- UglifyJS会移除掉所有的if分支,因为“production” !== "production"永远返回false
它存在与webpack中,只要安装了webpack就可以使用,如下
new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"production"', }),
打包时间
构建时间记录(3次):6.78s, 6.95s, 7.31s
通过上述三次时间的对比,可以看出使用DefinePlugin的效果并不明显。
4. 插件:UglifyJS-webpack-plugin
插件说明及使用
该插件支持parallel
字段,可以进行并行化构建,可以显着加速构建。
安装方式:
npm i -D uglifyjs-webpack-plugin 或者 yarn add uglifyjs-webpack-plugin -D
PS:值得注意的是,该插件已经在webpack4
之后的版本被移除了,所以如果你正在使用的是webpack4,那使用uglifyjs-webpack-plugin将会报错。好吧,既然移除了,我们就不再使用了。感兴趣的小伙伴请参考官网配置。
5. 一个优秀的打包分析软件:webpack-bundle-analyzer
插件说明及使用
该插件可以生成项目中所以bundle的treemap图,让优化一目了然所以的包大小以及如何优化的方向。如下图:
安装方式:
npm i -D webpack-bundle-analyzer 或者 yarn add webpack-bundle-analyzer -D
使用:
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
在对应的webpackCommonPlugins中添加即可:
new BundleAnalyzerPlugin(),
如何分析
首先,我们可以观察一下,这个树形图中都包含了哪些内容:
- 每个打包后的bumdle文件中包含了html, css, img, js, modules, components等;
- 每个bundle文件中,列出了具体的大小,包含start size, parsed size, gzip size具体的数值,便于使用者针对性的进行优化;
3个size大小分别表示:
- start size: 原始大小,没有经过处理的;
- parsed size: 处理之后的大小;
- gzipped size: 经过gzip压缩后的大小;
文件出现的面积越大,打包后的文件就越大,针对性进行优化即可。
6. 一些优化的小动作
动作1:
我们给不需要loader进行处理的文件加上exclude,如:给eslint-loader加上exclude: [resolve('node_modules')], 这样一来他就不会去加载node_modules中的文件了。
{
enforce: 'pre', test: /\.js$/, include: [resolve('src')], exclude: [resolve('node_modules')], loader: 'eslint-loader',},
动作2:
给babel-loader加上缓存参数:cacheDirectory=true。
{ test: /\.js$/, loader: 'babel-loader?cacheDirectory=true', include: [resolve('src')],},
打包时间:
打包时间记录(3次):6.55s, 6.26s, 6.59s
从上述时间上来看,总体是减少了,但仍旧效果不明显。
7. devServer配置
思考:
随着项目后期越来越大,每次build的时间必然会比较长,这样会严重降低开发效率。并且注意:我们没有办法再打包时就去调试http请求。
针对这个问题,我们就可以在开发过程中引入webpack-dev-server,可参考官网:www.webpackjs.com/configurati…,它可以帮助我们在开发过程中起一个小型的服务器,设置为热更新模式,方便我们快速开发,提高效率。
安装:
npm i -D webpack-dev-server 或者 yarn add webpack-dev-server -D
使用:
在webpack的配置文件中添加如下内容:
devServer: { contentBase: resolve('dist'), hot: true, open: true, },
然后再在packge.json中添加:
"dev": "webpack-dev-server",
此时执行npm run dev,就可以看到我们启动的服务了,且对于开发中的更改也会及时更新出来,根目录下不会产生dist文件夹,而是直接打包在了内存上
但我们不想因为修改了一小块东西就整体进行刷新,那么HotModuleReplacementPlugin()就可以帮助我们实现局部更新功能,所以加上就可以了。
plugins: [
new webpack.HotModuleReplacementPlugin(),
],