MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Webpack DllPlugin 与其他插件的配合使用

2021-06-233.1k 阅读

Webpack DllPlugin 基础介绍

Webpack 是现代前端开发中广泛使用的打包工具,它能够将各种资源(如 JavaScript、CSS、图片等)进行打包和优化。在项目开发过程中,随着代码量的增加,打包时间也会变得越来越长。Webpack DllPlugin 便是为了解决这一问题而诞生的。

DllPlugin(动态链接库插件)的核心思想是将一些不常变化的第三方库(如 React、Vue、lodash 等)提前打包成一个或多个 DLL(Dynamic Link Library,动态链接库)文件。在后续的项目打包过程中,Webpack 不再需要重复打包这些库,而是直接引用已经生成的 DLL 文件,从而显著提高打包速度。

下面我们来看一个简单的示例,假设我们有一个基于 React 的项目,我们想要使用 DllPlugin 来处理 React 和 React - DOM 库。

首先,在项目根目录下创建一个 webpack.dll.js 文件,内容如下:

const path = require('path');
const webpack = require('webpack');

module.exports = {
    entry: {
        // 这里将 React 和 React - DOM 作为入口
        react: ['react', 'react - dom']
    },
    output: {
        path: path.resolve(__dirname, 'dll'),
        filename: '[name].dll.js',
        library: '[name]_[hash]'
    },
    plugins: [
        new webpack.DllPlugin({
            path: path.join(__dirname, 'dll', '[name].manifest.json'),
            name: '[name]_[hash]'
        })
    ]
};

上述代码中:

  • entry 定义了要打包进 DLL 的库,这里是 reactreact - dom
  • output 配置了 DLL 文件的输出路径和文件名,library 定义了 DLL 的全局变量名。
  • webpack.DllPlugin 插件用于生成 manifest 文件,这个文件记录了 DLL 中各个模块的信息,Webpack 在后续打包时会利用它来找到对应的模块。

然后,在 package.json 中添加一个脚本:

{
    "scripts": {
        "dll": "webpack --config webpack.dll.js"
    }
}

运行 npm run dll 命令,就会在 dll 目录下生成 react.dll.jsreact.manifest.json 文件。

在项目的主 Webpack 配置文件(通常是 webpack.config.js)中,我们需要引入 DllReferencePlugin 来告诉 Webpack 如何引用这些 DLL。

const path = require('path');
const webpack = require('webpack');

module.exports = {
    // 其他配置项...
    plugins: [
        new webpack.DllReferencePlugin({
            context: __dirname,
            manifest: require('./dll/react.manifest.json')
        })
    ]
};

这样,在后续的项目打包过程中,Webpack 就会直接引用 react.dll.js 中的 React 和 React - DOM 模块,而不会重复打包它们,大大加快了打包速度。

Webpack DllPlugin 与 HtmlWebpackPlugin 的配合使用

HtmlWebpackPlugin 是一个非常实用的 Webpack 插件,它能够自动生成 HTML 文件,并将打包后的 JavaScript 和 CSS 文件插入到 HTML 中。当与 DllPlugin 配合使用时,我们需要确保生成的 HTML 文件中正确引入了 DLL 文件。

首先,安装 html - webpack - plugin

npm install html - webpack - plugin --save - dev

webpack.config.js 中配置 HtmlWebpackPlugin

const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html - webpack - plugin');

module.exports = {
    // 其他配置项...
    plugins: [
        new webpack.DllReferencePlugin({
            context: __dirname,
            manifest: require('./dll/react.manifest.json')
        }),
        new HtmlWebpackPlugin({
            template: './src/index.html',
            filename: 'index.html'
        })
    ]
};

上述配置中,HtmlWebpackPlugin 会根据 template 指定的模板文件(src/index.html)生成最终的 HTML 文件,并将打包后的资源插入其中。然而,此时生成的 HTML 文件并没有引入 DLL 文件。

为了让 HtmlWebpackPlugin 引入 DLL 文件,我们可以使用 html - webpack - inline - source - plugin 插件,它可以将指定的文件内联到 HTML 中。

安装 html - webpack - inline - source - plugin

npm install html - webpack - inline - source - plugin --save - dev

修改 webpack.config.js

const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html - webpack - plugin');
const InlineSourcePlugin = require('html - webpack - inline - source - plugin');

module.exports = {
    // 其他配置项...
    plugins: [
        new webpack.DllReferencePlugin({
            context: __dirname,
            manifest: require('./dll/react.manifest.json')
        }),
        new HtmlWebpackPlugin({
            template: './src/index.html',
            filename: 'index.html',
            inlineSource: '.(js|css)$'
        }),
        new InlineSourcePlugin(HtmlWebpackPlugin)
    ]
};

上述配置中,inlineSource 选项指定了哪些文件要内联到 HTML 中,HtmlWebpackPlugin 会在生成 HTML 时,将符合规则的文件内联进来。InlineSourcePlugin 则是实际执行内联操作的插件。

假设我们有一个简单的 src/index.html 模板文件:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF - 8">
    <meta name="viewport" content="width=device - width, initial - scale=1.0">
    <title>Webpack DllPlugin with HtmlWebpackPlugin</title>
</head>

<body>
    <div id="root"></div>
</body>

</html>

当 Webpack 打包完成后,生成的 index.html 文件会自动将 react.dll.js 内联进来(如果配置正确),确保页面在加载时能够正确引用到 React 和 React - DOM 库。

Webpack DllPlugin 与 CleanWebpackPlugin 的配合使用

CleanWebpackPlugin 用于在每次打包前清理输出目录,以确保输出目录中只包含最新打包生成的文件。当与 DllPlugin 配合使用时,我们需要注意清理的范围,避免误删 DLL 文件。

首先,安装 clean - webpack - plugin

npm install clean - webpack - plugin --save - dev

webpack.config.js 中配置 CleanWebpackPlugin

const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require('clean - webpack - plugin');

module.exports = {
    output: {
        path: path.resolve(__dirname, 'dist')
    },
    plugins: [
        new CleanWebpackPlugin(['dist'], {
            exclude: ['dll']
        }),
        new webpack.DllReferencePlugin({
            context: __dirname,
            manifest: require('./dll/react.manifest.json')
        })
    ]
};

在上述配置中,CleanWebpackPlugin 会在打包前清理 dist 目录,但通过 exclude 选项,我们将 dll 目录排除在外,这样就不会误删 DLL 文件。如果不进行这样的排除,每次打包时 DLL 文件都会被删除,导致后续引用失败。

Webpack DllPlugin 与 MiniCssExtractPlugin 的配合使用

MiniCssExtractPlugin 用于将 CSS 从 JavaScript 中提取出来,生成单独的 CSS 文件。在使用 DllPlugin 的项目中,如果项目中有依赖的 CSS 文件(例如 React 组件库可能带有自己的 CSS 样式),我们需要确保这些 CSS 也能正确处理。

首先,安装 mini - css - extract - plugin

npm install mini - css - extract - plugin --save - dev

假设我们有一个 React 组件库,它的 CSS 文件通过 import 引入到项目中,我们在 webpack.config.js 中进行如下配置:

const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini - css - extract - plugin');

module.exports = {
    // 其他配置项...
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [MiniCssExtractPlugin.loader, 'css - loader']
            }
        ]
    },
    plugins: [
        new webpack.DllReferencePlugin({
            context: __dirname,
            manifest: require('./dll/react.manifest.json')
        }),
        new MiniCssExtractPlugin({
            filename: '[name].css',
            chunkFilename: '[id].css'
        })
    ]
};

上述配置中,MiniCssExtractPlugin 会将 CSS 文件提取出来,生成单独的 CSS 文件。如果 DLL 中包含的库引入了 CSS 文件,这些 CSS 文件也会被正确提取。例如,如果 React 组件库有自己的样式文件,在引用 DLL 中的组件时,其样式也会被正确加载。

Webpack DllPlugin 与 OptimizeCSSAssetsPlugin 的配合使用

OptimizeCSSAssetsPlugin 用于优化和压缩 CSS 文件。当与 DllPlugin 配合使用时,我们可以进一步提高项目中 CSS 文件的性能。

首先,安装 optimize - css - assets - plugin

npm install optimize - css - assets - plugin --save - dev

webpack.config.js 中配置 OptimizeCSSAssetsPlugin

const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini - css - extract - plugin');
const OptimizeCSSAssetsPlugin = require('optimize - css - assets - plugin');

module.exports = {
    // 其他配置项...
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [MiniCssExtractPlugin.loader, 'css - loader']
            }
        ]
    },
    plugins: [
        new webpack.DllReferencePlugin({
            context: __dirname,
            manifest: require('./dll/react.manifest.json')
        }),
        new MiniCssExtractPlugin({
            filename: '[name].css',
            chunkFilename: '[id].css'
        }),
        new OptimizeCSSAssetsPlugin({})
    ]
};

OptimizeCSSAssetsPlugin 会在 CSS 文件生成后对其进行优化,例如压缩 CSS 代码,去除冗余的空格、注释等,从而减小 CSS 文件的体积,提高页面加载速度。即使是 DLL 中相关库引入的 CSS 文件,也会经过 OptimizeCSSAssetsPlugin 的优化。

Webpack DllPlugin 与 TerserPlugin 的配合使用

TerserPlugin 是 Webpack 内置的用于压缩 JavaScript 代码的插件。在使用 DllPlugin 的项目中,我们可以通过配置 TerserPlugin 来进一步优化 DLL 文件和项目代码的体积。

webpack.config.js 中配置 TerserPlugin

const path = require('path');
const webpack = require('webpack');
const TerserPlugin = require('terser - webpack - plugin');

module.exports = {
    // 其他配置项...
    optimization: {
        minimizer: [
            new TerserPlugin({
                parallel: true,
                terserOptions: {
                    compress: {
                        drop_console: true
                    }
                }
            })
        ]
    },
    plugins: [
        new webpack.DllReferencePlugin({
            context: __dirname,
            manifest: require('./dll/react.manifest.json')
        })
    ]
};

上述配置中,TerserPluginparallel 选项开启了并行压缩,提高压缩速度。terserOptions.compress.drop_console 选项会移除代码中的 console.log 等调试语句,进一步减小代码体积。无论是项目自身的 JavaScript 代码,还是 DLL 文件中的代码,都会经过 TerserPlugin 的压缩和优化。

Webpack DllPlugin 与 BundleAnalyzerPlugin 的配合使用

BundleAnalyzerPlugin 用于分析 Webpack 打包后的文件大小和依赖关系,帮助我们找出体积较大的模块,优化项目打包。当与 DllPlugin 配合使用时,它可以让我们更清楚地了解 DLL 文件的组成和影响。

首先,安装 webpack - bundle - analyzer

npm install webpack - bundle - analyzer --save - dev

webpack.config.js 中配置 BundleAnalyzerPlugin

const path = require('path');
const webpack = require('webpack');
const BundleAnalyzerPlugin = require('webpack - bundle - analyzer').BundleAnalyzerPlugin;

module.exports = {
    // 其他配置项...
    plugins: [
        new webpack.DllReferencePlugin({
            context: __dirname,
            manifest: require('./dll/react.manifest.json')
        }),
        new BundleAnalyzerPlugin()
    ]
};

运行 Webpack 打包后,BundleAnalyzerPlugin 会打开一个浏览器窗口,展示项目打包后的模块依赖关系和文件大小。我们可以通过这个可视化界面,查看 DLL 文件中各个模块的大小,以及它们对整个项目体积的影响。例如,如果发现某个 DLL 中的模块体积过大,可以考虑进一步优化该模块,或者将其从 DLL 中移除,单独处理。

实际项目中的综合应用案例

假设我们正在开发一个大型的 React 项目,除了 React 和 React - DOM 外,还使用了 Redux、lodash 等库。我们希望使用 DllPlugin 来优化打包速度,并结合上述多个插件进行综合优化。

  1. 配置 DLL 打包: 在 webpack.dll.js 中:
const path = require('path');
const webpack = require('webpack');

module.exports = {
    entry: {
        react: ['react', 'react - dom'],
        redux: ['redux','react - redux'],
        lodash: ['lodash']
    },
    output: {
        path: path.resolve(__dirname, 'dll'),
        filename: '[name].dll.js',
        library: '[name]_[hash]'
    },
    plugins: [
        new webpack.DllPlugin({
            path: path.join(__dirname, 'dll', '[name].manifest.json'),
            name: '[name]_[hash]'
        })
    ]
};
  1. 主 Webpack 配置: 在 webpack.config.js 中:
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html - webpack - plugin');
const InlineSourcePlugin = require('html - webpack - inline - source - plugin');
const CleanWebpackPlugin = require('clean - webpack - plugin');
const MiniCssExtractPlugin = require('mini - css - extract - plugin');
const OptimizeCSSAssetsPlugin = require('optimize - css - assets - plugin');
const TerserPlugin = require('terser - webpack - plugin');
const BundleAnalyzerPlugin = require('webpack - bundle - analyzer').BundleAnalyzerPlugin;

module.exports = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel - loader',
                    options: {
                        presets: ['@babel/preset - env', '@babel/preset - react']
                    }
                }
            },
            {
                test: /\.css$/,
                use: [MiniCssExtractPlugin.loader, 'css - loader']
            }
        ]
    },
    optimization: {
        minimizer: [
            new TerserPlugin({
                parallel: true,
                terserOptions: {
                    compress: {
                        drop_console: true
                    }
                }
            })
        ]
    },
    plugins: [
        new CleanWebpackPlugin(['dist'], {
            exclude: ['dll']
        }),
        new webpack.DllReferencePlugin({
            context: __dirname,
            manifest: require('./dll/react.manifest.json')
        }),
        new webpack.DllReferencePlugin({
            context: __dirname,
            manifest: require('./dll/redux.manifest.json')
        }),
        new webpack.DllReferencePlugin({
            context: __dirname,
            manifest: require('./dll/lodash.manifest.json')
        }),
        new MiniCssExtractPlugin({
            filename: '[name].css',
            chunkFilename: '[id].css'
        }),
        new OptimizeCSSAssetsPlugin({}),
        new HtmlWebpackPlugin({
            template: './src/index.html',
            filename: 'index.html',
            inlineSource: '.(js|css)$'
        }),
        new InlineSourcePlugin(HtmlWebpackPlugin),
        new BundleAnalyzerPlugin()
    ]
};

在这个配置中:

  • 首先通过 CleanWebpackPlugin 清理输出目录,同时排除 DLL 目录。
  • 然后使用 DllReferencePlugin 引用多个 DLL 文件。
  • MiniCssExtractPluginOptimizeCSSAssetsPlugin 处理和优化 CSS 文件。
  • HtmlWebpackPluginInlineSourcePlugin 生成并优化 HTML 文件,内联相关资源。
  • TerserPlugin 压缩 JavaScript 代码。
  • BundleAnalyzerPlugin 用于分析打包后的文件依赖和大小。

通过这样的综合配置,我们可以在大型 React 项目中有效地利用 DllPlugin 提高打包速度,并结合其他插件对项目的各个方面进行优化,提升项目的整体性能。

注意事项与常见问题

  1. DLL 文件更新:当 DLL 中包含的库版本发生变化时,需要重新运行 npm run dll 命令重新打包 DLL 文件,并更新对应的 manifest 文件。否则,项目可能会因为引用旧版本的库而出现兼容性问题。
  2. 全局变量冲突:由于 DLL 文件通过全局变量暴露模块,可能会与项目中的其他全局变量发生冲突。在设置 library 选项时,要确保其全局变量名具有足够的唯一性。
  3. 多入口项目:对于多入口的 Webpack 项目,每个入口都需要正确引用 DLL 文件。可以通过在每个入口对应的配置中添加 DllReferencePlugin 来解决。
  4. 调试问题:在开发过程中,如果发现项目引用 DLL 文件出现问题,例如模块找不到等错误,可以通过查看 Webpack 的输出日志,以及检查 manifest 文件和 DLL 文件的路径是否正确来排查问题。

总之,Webpack DllPlugin 与其他插件的配合使用可以极大地提升前端项目的开发和部署效率,但在使用过程中需要注意各种细节,确保配置的正确性和项目的稳定性。通过合理运用这些插件,我们能够打造出性能更优、开发体验更好的前端项目。