Skip to content

Vermouth1995/webpack-optimization

Repository files navigation

webpack 构建速度及体积优化策略

一、统计分析

初级分析
  • 使用 webpack 内置 stats
    webpack --config webpack.config.js --json > stats.json
    特点:输出构建的一些统计信息
    缺点:颗粒度较大,无法发现具体问题

速度分析
  • 使用 speed-measure-webpack-plugin
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
const smp = new SpeedMeasurePlugin();
const webpackConfig = smp.wrap({
    ···
    plugins: [
        new MyPlugin(),
        new MyOtherPlugin()
    ]
    ···
});

1、分析记录整个打包过程总耗时
2、分析记录每个插件和 loader 的耗时情况

体积分析
  • 使用 webpack-bundle-analyzer
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
    ···
    plugins: [
        new BundleAnalyzerPlugin()
    ]
    ···
};

构建完成后会自动显示文件大小
1、分析依赖的第三方模块体积大小
2、分析业务里的组件体积大小

  • 使用 jarvis
const Jarvis = require('webpack-jarvis');
module.exports = {
    ···
    plugins: [
        new Jarvis({
            port: 1337 // optional: set a port
        })
    ]
    ···
};

好看!

二、优化手段

1、使用高版本 webpack

2、多进程并行解析资源模块

  • thread-loader

原理:每次 webpack 解析一个模块,thread-loader 会将它及它的依赖分配给 worker 进程。

module.exports = {
    ···
    module: {
        rules: [{
            test: /\.js$/,
            use: [ 'thread-loader', 'babel-loader' ]
        }]
    }
    ···
};
  • HappyPack

原理:每次 webpack 解析一个模块,HappyPack 会将它及它的依赖分配到 worker 进程。

const HappyPack = require('happypack');
module.exports = {
    ···
    plugins: [
        new HappyPack({
            id: 'jsx',
            threads: 4,
            loaders: [ 'babel-loader' ]
        }),
        new HappyPack({
            id: 'styles',
            threads: 2,
            loaders: [ 'style-loader', 'css-loader', 'less-loader' ]
        })
    ]
    ···
};

3、多进程并行压缩代码

  • 使用 webpack-parallel-uglify-plugin 插件
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
module.exports = {
    ···
    plugins: [
        new ParallelUglifyPlugin({
            uglifyJS: {
                output: {
                    beautify: false,
                    comments: false
                },
                compress: {
                    warnings: false,
                    drop_console: true,
                    collapse_vars: true
                }
            }
        })
    ]
    ···
};
  • 使用 uglify-webpack-plugin 插件,开启 parallel 参数(老的 webpack 版本使用,不支持压缩ES6的语法)
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
    ···
    optimization: {
        minimizer: [new UglifyJsPlugin()],
    }
    ···
};
  • 使用 terser-webpack-plugin 插件,开启 parallel 参数(新的 webpack 版本使用,支持压缩ES6的语法)
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
    ···
    optimization: {
        minimizer: [
            new TerserPlugin({
                parallel: true
            })
        ],
    }
    ···
};

4、分包

  • 使用 html-webpack-externals-plugin,设置 externals

原理:将 react,react-dom 等公共基础包通过 cdn 方式引入,不打入 bundle 中

const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');
module.exports = {
    ···
	plugins: [
        new HtmlWebpackExternalsPlugin({
            externals: [{
                module: 'react',
                entry: 'https://unpkg.com/react@16/umd/react.development.js',
                global: 'React'
            }]
        })
    ]
    ···
};
  • 预编译资源模块,使用 DLLPlugin 分包,DllReferencePlugin对 manifest.json 引用

原理:将 react,react-dom 等公共基础包打包成一个文件

const path = require('path');
const webpack = require('webpack');
module.exports = {
    entry: {
        library: ['react','react-dom']
    },
    output: {
        filename: '[name]_[chunkhash].dll.js',
        path: path.resolve(__dirname,'./build/library'),
        library: '[name]'
    },
    plugins: [
        new webpack.DllPlugin({
            name: '[name]_[hash]',
            path: path.resolve(__dirname,'./build/library/[name].json')
        })
    ]
};
module.exports = {
    ···
    plugins: [
        new webpack.DllReferencePlugin({
            manifest: require('./build/library/library.json')
        })
    ]
    ···
};

5、缓存(提升二次构建速度)

  • babel-loader 开启缓存
module.exports = {
    ···
	module: {
        rules: [{
            test: /\.js$/,
            use: [ 'babel-loader?cacheDirectory=true' ]
        }]
    }
    ···
};
  • terset-webpack-plugin 开启缓存
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
    ···
    optimization: {
        minimizer: [
            new TerserPlugin({
                parallel: true,
                cache: true
            })
        ],
    }
    ···
};
  • 使用 cache-loader 或者 hard-source-webpack-plugin
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
module.exports = {
    ···
    plugins: [ new HardSourceWebpackPlugin() ]
    ···
};

6、缩小构建目标

  • 尽可能少的构建模块
module.exports = {
    ···
	module: {
        rules: [{
            test: /\.js$/,
            use: [ 'babel-loader' ],
            exclude: 'node_modules'
        }]
    }
    ···
};
  • 减少文件搜索范围

(1)优化 resolve.modules 配置(减少模块搜索层级)
(2)优化 resolve.mainFields 配置
(3)优化 resolve.extensions 配置
(4)合理使用 alias

module.exports = {
    ···
    resolve: {
        modules: [path.resolve(__dirname, 'node_modules')],
        mainFields: ['main'],
        extensions: ['.js'],
        alias: {
            'react': path.resolve(__dirname, './node_modules/react/umd/react.production.min.js'),
            'react-dom': path.resolve(__dirname, './node_modules/react-dom/umd/react-dom.production.min.js')
        }
    }
    ···
};

7、开启 tree-shaking

  • 什么是 tree-shaking

通过工具"摇"我们的 js 文件,将其中用不到的代码"摇"掉,是一个性能优化的范畴。具体来说,在 webpack 项目中,有一个入口文件,相当于一棵树的主干,入口文件有很多依赖的模块,相当于树枝。实际情况中,虽然依赖了某个模块,但其实只使用其中的某些功能。通过 tree-shaking,将没有使用的模块摇掉,这样来达到删除无用代码的目的。

  • 怎么使用
    webpack 默认支持,.babelrc 文件设置 modules: false 即可。(production mode 下默认开启)

  • 擦除无用 css

const glob = require('glob');
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const PurgecssPlugin = require('purgecss-webpack-plugin');
module.exports = {
    ···
    module: {
        rules: [{
            test: /\.css$/,
            use: [ MiniCssExtractPlugin.loader, 'css-loader' ]
        }]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name]_[contenthash:8].css'
        }),
        new PurgecssPlugin({
            paths: glob.sync(`${path.join(__dirname, 'src')}/**/*`,  { nodir: true }),
        })
    ]
    ···
};

8、图片压缩

  • 配置 image-webpack-loader
module.exports = {
    ···
    rules: [{
        test: /\.(gif|png|jpe?g|svg)$/i,
        use: [
            'file-loader',
            {
                loader: 'image-webpack-loader',
                options: { disable: true }
            },
        ],
    }]
   ···
};

About

✨ webpack 构建速度及体积优化策略

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages