webpack 构建优化

这篇主要介绍四类 webpack 构建优化方法,分别是:

  • 缩小查找范围
  • 减少打包文件
  • 缓存
  • 多进程

缩小查找范围

使用 loader 时给定具体范围: include

module.exports = {
  module: {
    rules: [
        {
            test: /\.js$/,
            use: {
                loader:'babel-loader'
            },
            include: [
                path.resolve(__dirname, 'src')
            ],
        }
    }
}

使用 module.noParse 让某些非 AMD/CommonJS 模块不参与解析

module.exports = {
  module: {
    noParse: /jquery|lodash/
  }
};

使用 resolve.alias 缩短包的查找过程

module.exports = {
    resolve: {
        'vue$': 'vue/dist/vue.esm.js',
    }
};

使用 resolve.modules 缩短包目标位置的查找过程

module.exports = {
    resolve: {
        modules: [
            path.resolve(__dirname,'src')
            path.resolve(__dirname, 'node_modules'),
        ]
    }
};

使用 resolve.mainFields 缩短包在 package.json 中的 main 字段的查找过程

module.exports = {
    resolve: {
        mainFields: ['main', 'module']
    }
};

减少打包文件

使用 optimzation.splitChunks 分割出公共代码

module.exports = {
    optimization: {
        splitChunks: {
            chunks: 'all',
            cacheGroups: {
                vendor: {
                    test: /[\\/]node_modules[\\/]/,
                    name:'vendor',
                    priority: 10,
                }
            }
        }
    }
};

使用DllPlugin 和 DllReferencePlugin 动态链接基本不改变的包

// webpack.dll.config.js
const path = require('path');
const webpack = require('webpack');

module.exports = {
    entry: {
        vendor: ['vue', 'lodash'], // 需要打包  的第三方库
    },
    output: {
        path: path.resolve(__dirname,'dist'),
        filename: 'dll.vendor.js',
        library: 'vendor_library',
    },
    plugins: [
        new webpack.DllPlugin({
            name: 'vendor_library',
            path: path.resolve(__dirname, 'dist', 'vendor-manifest.json'),
        }),
    ],
};

通过 webpack --config webpack.dll.config.js 打包出 dll 文件

然后在主配置中使用 DllReferencePlugin 引用

// webpack.config.js
const path = require('path');
const webpack = require('webpack');

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js',
    },
    plugins: [
        new webpack.DllReferencePlugin({
            manifest: require('./dist/vendor-manifest.json'),
        }),
    ],
};
<!-- 还需要在模板中引入 dll 文件 -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 引入动态链接文件 -->
    <script src="./vendor/vendor.dll.js"></script>
</body>
</html>

使用 externals 排除不必打包的文件库

module.exports = {
    externals: {
        vue: 'Vue',
        lodash: '_',
        $: 'jquery',
        jQuery: 'jquery',
    }
};

利用 tree-shaking 机制减小打包文件

  • 优先使用 ESM 模块的包,引入时只引入需要的方法

缓存

使用 cache-loader 缓存编译结果

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

使用 hard-source-webpack-plugin 缓存编译结果

这个插件可以缓存编译结果,并且在编译过程中,会将编译结果缓存到硬盘中,下次编译时,会优先读取缓存结果,加快编译速度。可以跨 dev 和 build 共享缓存

module.exports = {
    plugins: [
        new HardSourceWebpackPlugin(),
    ]
};

多进程

使用 happypack 多进程打包

const HappyPack = require('happypack');

module.exports = {
    module: {
        rules: [
            {
                test: /\.js$/,
                // 开启多进程打包
                use: 'happypack/loader?id=js',
            }
        ]
    },
    plugins: [
        new HappyPack({
            id: 'js',
            loaders: ['babel-loader'],
        })
    ]
}

使用 thread-loader 多进程打包

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