Webpack之DLL

文章目錄

引入

在我們的項目中,免不了要引入許多的第三方模塊。這些第三方模塊在打包的時候就會被打包進最後生成的文件之中。導致最後生成的文件過大的同時也增加了打包的時間。

這時我們就會想,如果這些第三方模塊能只打包一次,之後就直接使用這些打包好的模塊多好,畢竟這些第三方的模塊代碼不會有變動。

那麼,我們再次打包目標代碼時就不需要再從node_modules中去尋找第三方模塊,而是從我們預先打包出的文件中去尋找。

這種想法頗似動態鏈接庫(DLL)。

流程

由於需要單獨打包第三方模塊,也就意味着我們需要一個獨立的Webpack配置文件,我命名爲webpack.dll.js。內容如下:

const path = require('path');
const webpack = require('webpack');
module.exports = {
    mode: 'production',
    entry: {
        react: ['react', 'react-dom'],
        vendors: ['lodash']
    },
    output: {
        path: path.resolve(__dirname, 'dll'),
        filename: '[name].dll.js',
        library: '[name]'
    },
    plugins: [
        new webpack.DLLPliugin({
            name: '[name]',
            path: path.resolve(__dirname, 'dll')
        })
    ]
}

這個配置文件的含義是指將reactreact-dom打包進react.dll.js這個文件中,而lodash則打包進vendors.dll.js裏面。同時分別對外暴露兩個全局變量,分別叫做reactvendors

plugins內我使用了一個DLLPlugin,這個插件會在path字段所給出的路徑生成一個manifest.json。這個json文件包含了importrequire請求到模塊ID的映射。這個文件在正式打包項目代碼的時候會用到。

接下來就是該正式打包項目代碼了,項目代碼打包使用的Webpack配置文件如下:

const webpack = require('webpack');
const path = require('path');
module.exports = {
    ...,
    plugins: [
        new webpack.DLLReferencePlugin({
            manifest: path.resolve(__dirname, 'dll/react.manifest.js')
        }),
        new webpack.DLLReferencePlugin({
            manifest: path.resolve(__dirname, 'dll/vendors.manifest.js')
        })
    ]
}

通過引用 dll 的 manifest.json來把依賴的名稱映射到模塊的 id 上,之後再在需要的時候通過內置的 __webpack_require__ 函數來 require 他們。

接下來就可以直接打包了,應該會發現打包速度快了不少。

可是打開頁面會發現,頁面並不能顯示出任何內容,原因是因爲,打包出的dll.js們並沒有被網頁引入,因此我們還需要一個add-asset-html-webpack-plugin

這是一個爲html-webpack-plugin生成的頁面再添加靜態資源的插件。

以下是完整的plugins設置。

plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            template: './src/index.html'
        }),
        new webpack.HotModuleReplacementPlugin(),
        new AddAssetHtmlWebpackPlugin({
            filepath: path.resolve(__dirname, 'dll/react.dll.js')
        }),
        new AddAssetHtmlWebpackPlugin({
            filepath: path.resolve(__dirname, 'dll/vendors.dll.js')
        }),
        new webpack.DllReferencePlugin({
            manifest: path.resolve(__dirname, 'dll/react.manifest.json')
        }),
        new webpack.DllReferencePlugin({
            manifest: path.resolve(__dirname, 'dll/vendors.manifest.json')
        })
    ]

重新打包,這下子就可以在網頁中看到正常現實的內容了。

問題

可能大家也都注意到了,就是項目文件的Webpack配置文件的plugins寫的非常繁瑣。

假如,打包成DLL的文件又多了幾個。那麼這邊的plugins是不是還要自己手寫多個new webpack.DLLReferencePluginnew AddAssetHtmlWebpackPlugin

很顯然,有更智能的寫法。需要藉助Node.js的fs模塊。

const fs = require('fs');
const plugins = [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
        template: './src/index.html'
    }),
    new webpack.HotModuleReplacementPlugin(),
]

fs.readdirSync(path.resolve(__dirname, 'dll')).forEach(file => {
    const filePath = path.resolve(__dirname, `./dll/${file}`);
    if(/.*\.dll.js/.test(file)){
        plugins.push(
            new AddAssetHtmlWebpackPlugin({
                filepath: filePath
            })
        );
    }

    if(/.*\.manifest.json/.test(file)){
        plugins.push(
            new webpack.DllReferencePlugin({
                manifest: filePath
            })
        );
    }
});

這段代碼即是檢測dll目錄下都有哪些manifest.jsondll.js文件,並往plugins中添加相應插件。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章