使用 Webpack 的 DllPlugin 提高項目構建速度

本文介紹了 Webpack 中 DllPlugin 插件的使用,以及配合使用 AddAssetHtmlPlugin 將構建好的 JS 文件插入到 html 頁面中。html

本文 Demo 地址

本文項目代碼位置:源碼地址node

歡迎 Star!react


DLLPlugin 和 DllReferencePlugin 簡介

DLLPlugin 就是將包含大量複用模塊且不會頻繁更新的庫進行編譯,只須要編譯一次,編譯完成後存在指定的文件(這裏能夠稱爲動態連接庫)中。在以後的構建過程當中不會再對這些模塊進行編譯,而是直接使用 DllReferencePlugin 來引用動態連接庫的代碼。所以能夠大大提升構建速度。通常會對經常使用的第三方模塊使用這種方式,例如 react、react-dom、lodash 等等。只要這些模塊不升級更新,這些動態連接庫就不須要從新編譯。webpack


在 Webpack 中進行使用

須要插件

Webpack 已經內置了對動態連接庫的支持,須要經過兩個內置插件的配合使用。它們分別是:git

  • DllPlugin 插件:用於打包出一個個單獨的動態連接庫文件
  • DllReferencePlugin 插件:用於在主配置文件中去引入 DllPlugin 插件打包好的動態連接庫文件

建立項目

找一個空文件夾,打開命令行,執行命令

# 建立項目目錄
$ mkdir webpack-dll-demo

# 初始化 package.json 文件
$ npm init -y 

# 建立 src 文件夾
$ mkdir src

# 建立 public 文件夾
$ mkdir public

# 安裝須要用到的插件
$ npm install webpack webpack-cli html-webpacl-plugin clean-webpacl-plugin friendly-errors-webpack-plugin -D

# 安裝 lodash 插件,用於演示 DllPlugin 用法
$ npm install lodash
複製代碼

在 public 目錄下建立 index.html 文件

index.htmlgithub

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Webpak DllPlugin 的使用</title>
</head>
<body>
    <div id="root"></div>
</body>
</html>
複製代碼

在 src 目錄下建立 index.js 文件

index.jsweb

import { join } from 'lodash';

function createSpan(){
    const element = document.createElement('span');
    element.innerHTML = join(['Hello', 'DllPlugin'], ' , ');
    return element;
}

document.querySelector('#root').appendChild(createSpan());
複製代碼

當前項目目錄結構

webpack-prod-demo
|- /public
  |- index.html
|- /src
  |- index.js
|- package.json
複製代碼

使用 DllPlugin 和 DllReferencePlugin(分爲三步)

1、先編寫一個配置文件專門用來編譯生成動態連接庫(使用 DllPlugin)

webpack_dll.config.jsnpm

const path = require('path');
const webpack = require('webpack');
const CleanWebpaclPlugin = require('clean-webpack-plugin');
const FirendlyErrorePlugin = require('friendly-errors-webpack-plugin');

module.exports = {
    mode: 'production',
    entry: {
        // 將 lodash 模塊做爲入口編譯成動態連接庫
        lodash: ['lodash']
    },
    output: {
        // 指定生成文件所在目錄
        // 因爲每次打包生產環境時會清空 dist 文件夾,所以這裏我將它們存放在了 public 文件夾下
        path: path.resolve(__dirname, 'public/vendor'),
        // 指定文件名
        filename: '[name].dll.js',
        // 存放動態連接庫的全局變量名稱,例如對應 lodash 來講就是 lodash_dll_lib
        // 這個名稱須要與 DllPlugin 插件中的 name 屬性值對應起來
        // 之因此在前面 _dll_lib 是爲了防止全局變量衝突
        library: '[name]_dll_lib'
    },
    plugins: [
        new CleanWebpaclPlugin(['vendor'], {
            root: path.resolve(__dirname, 'public')
        }),
        new FirendlyErrorePlugin(),
        
        // 接入 DllPlugin
        new webpack.DllPlugin({
            // 描述動態連接庫的 manifest.json 文件輸出時的文件名稱
            // 因爲每次打包生產環境時會清空 dist 文件夾,所以這裏我將它們存放在了 public 文件夾下
            path: path.join(__dirname, 'public', 'vendor', '[name].manifest.json'),
            // 動態連接庫的全局變量名稱,須要和 output.library 中保持一致
            // 該字段的值也就是輸出的 manifest.json 文件 中 name 字段的值
            // 例如 lodash.manifest.json 中就有 "name": "lodash_dll_lib"
            name: '[name]_dll_lib'
        })
    ]
}
複製代碼

2、編寫配置文件用來打包項目(使用 DllReferencePlugin)

webpack.config.jsjson

const path = require('path');
const webpack = require('webpack');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const CleanWebpaclPlugin = require('clean-webpack-plugin');
const FirendlyErrorePlugin = require('friendly-errors-webpack-plugin');

module.exports = {
    mode: 'production',
    devtool: 'source-map',
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'build-[hash:5].js'
    },
    plugins: [
        new HTMLWebpackPlugin({
            title: 'Webpak DllPlugin 的使用',
            template: './public/index.html'
        }),
        new CleanWebpaclPlugin(['dist']),
        new FirendlyErrorePlugin(),
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify('production')
        }),
        // 告訴 Webpack 使用了哪些動態連接庫
        new webpack.DllReferencePlugin({
            // 描述 lodash 動態連接庫的文件內容
            manifest: require('./public/vendor/lodash.manifest.json')
        })
    ]
}
複製代碼

3、在 index.html 文件中引入動態連接庫

因爲動態連接庫咱們通常只編譯一次,以後就不用編譯,複用模塊都被打包到了動態連接庫中,所以入口的 index.js 文件中已經不包含這些模塊了,因此要在 index.html 中單獨引入。數組

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Webpak DllPlugin 的使用</title>
</head>
<body>
    <div id="root"></div>
    <script src="../public/vendor/lodash.dll.js"></script>
</body>
</html>
複製代碼

注意:因爲在打包項目的時候會清理掉 dist 文件,因此我將生成的動態連接庫放到了 public 目錄下,因此這裏是引入 public 下的動態連接庫。

咱們在 package.json 中添加兩條指令:

  • build:打包項目
  • build:dll:編譯生成動態連接庫

package.json

...
"scripts": {
    "build": "webpack --config webpack.config.js",
    "build:dll": "webpack --config webpack_dll.config.js"
}
...
複製代碼

運行

根據上面所說的三個步驟,Dll 的用法已經結束了。如今咱們運行一下看看結果。

打開命令行,執行命令

# 生成動態連接庫,只須要運行一次這個指令,之後打包項目不須要再執行這個指令
$ npm run build:dll

# 打包項目
$ npm run build
複製代碼

在瀏覽器中打開 dist 文件夾下的 index.html 文件,能夠看到瀏覽器上出現:Hello , DllPlugin。說明項目配置成功。

DllPlugin 和 DllReferencePlugin 分別作了什麼

運行 npm run build:dll 指令以後,能夠看到項目中 public 目錄下多出了一個 vendor 的文件夾,能夠看到其中包含兩個文件:

  • lodash.dll.js 裏面包含 lodash 的基礎運行環境,也就是 lodash 模塊
  • lodash.manifest.json 也是由 DllPlugin 生成出,用於描述動態連接庫文件中包含哪些模塊

lodash.dll.js

var lodash_dll_lib=...  // 此處代碼過多,進行省略
複製代碼

lodash.manifest.json

{"name":"lodash_dll_lib","content":{"./node_modules/lodash/lodash.js":{"id":1,"buildMeta":{"providedExports":true}},"./node_modules/webpack/buildin/global.js":{"id":2,"buildMeta":{"providedExports":true}},"./node_modules/webpack/buildin/module.js":{"id":3,"buildMeta":{"providedExports":true}}}}
複製代碼

對比以後能夠明白

  • 一個動態連接庫文件中包含了大量模塊的代碼,這些模塊存放在一個數組裏,用數組的索引號做爲 ID。 而且還經過 lodash_dll_lib 變量把本身暴露在了全局中,也就是能夠經過 window.lodash_dll_lib 能夠訪問到它裏面包含的模塊

  • manifest.json 文件清楚地描述了與其對應的 dll.js 文件中包含了哪些模塊,以及每一個模塊的路徑和 ID

至此,Dll 的使用以及配置完成了。可是這裏還有值得思考的地方:目前看來,項目能夠正常運行,可是如今動態連接庫是存放到 public 目錄下的,若是咱們須要將項目打包上線的話,如何可以讓動態連接庫自動也存放到 dist 目錄下呢?如何在咱們不手動添加腳本的狀況下,自動將動態連接庫引入到 index.html 文件中呢?若是有興趣的話,能夠繼續往下來看一看配合 add-asset-html-webpack-plugin 的使用。


add-asset-html-webpack-plugin 的使用

上面也已經說了,雖然 Dll 的使用和配置沒有問題了,可是還不是很滿意,打包的時候不能將動態連接庫自動的存放到 dist 文件夾,也不能自動在 html 文件中引入動態連接庫腳本。因此這時候 add-asset-html-webpack-plugin 就派上用場了。

安裝插件

$ npm install add-asset-html-webpack-plugin -D
複製代碼

使用

在 webpack.config.js 文件中進行使用

webpack.config.js

...;
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');

module.exports = {
    ...,
    plugins: [
        ...,
        // 該插件將把給定的 JS 或 CSS 文件添加到 webpack 配置的文件中,並將其放入資源列表 html webpack插件注入到生成的 html 中。
        new AddAssetHtmlPlugin([
            {
                // 要添加到編譯中的文件的絕對路徑,以及生成的HTML文件。支持globby字符串
                filepath: require.resolve(path.resolve(__dirname, 'public/vendor/lodash.dll.js')),
                // 文件輸出目錄
                outputPath: 'vendor',
                // 腳本或連接標記的公共路徑
                publicPath: 'vendor'
            }
        ])
    ]
}
複製代碼

此時能夠刪除 index.html 文件中手動引入的腳本了

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Webpak DllPlugin 的使用</title>
</head>
<body>
    <div id="root"></div>
    <!-- 刪除下面這行引入腳本 -->
-    <script src="../public/vendor/lodash.dll.js"></script>
</body>
</html>
複製代碼

運行項目

打開命令行,執行命令:

# 打包項目
$ npm run build
複製代碼
  • 如今查看項目中 dist 文件夾,能夠看到 public 目錄下 vendor 文件夾中的 js 文件已經所有自動拷貝到 dist 目錄中的 vendor 文件夾下了

  • 打開 dist 文件夾中的 index.html 文件,能夠看到已經自動將生成的腳本文件引入了

  • 在瀏覽器中打開 index.html,能夠看到 'Hello , DllPlugin' 也可以正常顯示

add-asset-html-webpack-plugin 更多配置請參考 github 地址:AddAssetHtmlPlugin 配置


總結

  • Dll 動態連接庫的使用能夠提升項目構建速度,由於對於大量複用的模塊能夠提早進行編譯,且只須要編譯一次,以後的開發中,使用這些模塊的地方都不會再從新進行編譯

  • DllPlugin 和 DllReferencePlugin 須要配合使用

    • DllPlugin 用於打包出一個個單獨的動態連接庫文件並生成對應的主清單文件用於描述動態連接庫中包含哪些模塊
    • DllReferencePlugin 用於在主清單文件中去引入 DllPlugin 插件打包好的動態連接庫文件
  • 可使用 AddAssetHtmlPlugin 將生成的動態連接庫文件拷貝到出口文件夾下,而後 HTMLWebpackPlugin 就會自動的將腳本文件注入到生成的 html 文件中去

  • **注意:**如想測試一下構建速度是否有提高,能夠將 webpack.config.js 中的 DllReferencePlugin 和 AddAssetHtmlPlugin 使用註釋起來,運行 npm run build,觀察打包時間;再將註釋打開,運行 npm run build,觀察打包時間,進行對比,便可發現區別

如是第一次打包,請先運行 npm run build:dll 生成動態連接庫。

本文 Demo 地址:源碼地址。 歡迎 Star!

相關文章
相關標籤/搜索