天天看點

Webpack dll優化實戰DLL是什麼,用它來幹啥?前端應用場景項目實戰如何将vendors_dll.js插入html?

DLL是什麼,用它來幹啥?

  DLL(Dynamic Link Libray)原來特指windows系統中實作共享函數庫的一種方式,擴充名通常為.dll。玩過老windows遊戲的同學應該對這種檔案不陌生,很多遊戲的安裝盤下就有很多.dll的檔案。DLL通常是已經編譯、連結的二進制檔案,友善程式直接調用。

前端應用場景

  在大型項目的開發過程中,往往會用到很多公共庫,公共庫的内容不同于業務代碼,在很長的一個時間周期内都不會有改動。這部分公共庫通常會被打包在commonChunk中,webpack配置節選如下:

optimization: {
        splitChunks: {
            minSize: 1000000,
            cacheGroups: {
                vendor: {
                    name: "common",
                    test: /[\\/]node_modules[\\/]/,
                    chunks: "initial",
                    minSize: 30000,
                    minChunks: 1,
                    priority: 8
                }
            },
        }
    },
           

  這樣可以把長時間不變的公共包内容打包進一個名為common的chunk中,同業務代碼進行隔離,生成穩定的緩存key,以便浏覽器端實作緩存。但是這也帶來一個問題:盡管使用者端實作了緩存,但是我們在打包的時候,webpack依然會對所有用到的公共包進行周遊,解析,處理。嚴重影響打包速度。

  webpack中的DLL打包優化,同windows中的DLL的原理類似。通過另寫一份打包配置,把長期不變的公共包内容單獨打包,然後在業務代碼打包時,通用webpack内置插件DllReferencePlugin引用之前打包的動态連結内容,而不是每次都打包同樣的公共包内容,進而加快打包速度。

項目實戰

接下來筆者将以自己項目為例,進行webpack dll優化。

使用原來的打包配置,結果如下:

Webpack dll優化實戰DLL是什麼,用它來幹啥?前端應用場景項目實戰如何将vendors_dll.js插入html?

打包耗時:

Webpack dll優化實戰DLL是什麼,用它來幹啥?前端應用場景項目實戰如何将vendors_dll.js插入html?

接下來,在項目中添加webpack.dll.config.js檔案,對一些固定不變的内容提前打成dll資源:

const webpack = require('webpack')
const library = '[name]_dll'
const path = require('path');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  entry: {
    vendors: ['react', 'mobx', 'mobx-react', 'antd', 'socket.io-client']
  },

  output: {
    filename: '[name]_dll.js',
    path: path.resolve(__dirname, 'dist'),
    //publicPath: path.resolve(__dirname, 'dist'),
    publicPath: './',
    library
  },

  plugins: [
    new webpack.DllPlugin({
      path: path.join(__dirname, 'dist/[name]-manifest.json'),
      // This must match the output.library option above
      name: library
    }),
    new BundleAnalyzerPlugin({
        analyzerPort: 8899
    })
  ],
}
           

打包結果如下(vendors_dll.js: 685kb):

Webpack dll優化實戰DLL是什麼,用它來幹啥?前端應用場景項目實戰如何将vendors_dll.js插入html?

這個dll包壓縮後還有600多kb!

接下來我們再對項目代碼進行打包,在webpack配置檔案中添加如下插件:

plugins: [
        ......
        //  動态連結庫
        new webpack.DllReferencePlugin({
            context: __dirname,
            manifest: require('./dist/vendors-manifest.json')
        }),
        ......
    ]    
           

打包大小:

Webpack dll優化實戰DLL是什麼,用它來幹啥?前端應用場景項目實戰如何将vendors_dll.js插入html?

打包時間:

Webpack dll優化實戰DLL是什麼,用它來幹啥?前端應用場景項目實戰如何将vendors_dll.js插入html?

  通過如上實驗我們可以發現,使用DllReferencePlugin插件後,打包時間大幅縮小,從原來的16s縮減到10秒左右,這是由于相當部分的資源已經提前建構好,在業務代碼改變的時候,自然就不用重複打包浪費時間了。但同時暴露出一個問題:原先的打包方式打封包件的體積大概是378kb,使用dll之後,打包體積是dll資源與業務代碼之和(685kb+92kb),整整大了兩倍。要知道,dll檔案浏覽器也是需要下載下傳的,這樣完全是無法接受的。通過對打包内容的分析,我們可以發現webpack.dll.config.js檔案的入口不是業務代碼,直接是各個公共包,這就意味着dll顯然是無法使用treeShake等等優化特性的,比如antd這個包,優化前使用treeShake按需加載,打包體積隻有168kb左右,而在dll包中,antd整個包都進行了引入,體積膨脹到了250kb左右。由此我們可以得到一個重要的實戰經驗:針對antd,lodash等等可以使用按需加載的公共庫,不能提前打包在dll中,像react,mobx等等必然會全盤引入的公共庫才是最适合放入dll中的。

  接下來我們修改一下webpack.dll.config.js的entry配置(删掉antd):

entry: {
    vendors: ['react', 'mobx', 'mobx-react', 'socket.io-client']
  },
           

dll打包結果(vendors_dll:84kb):

Webpack dll優化實戰DLL是什麼,用它來幹啥?前端應用場景項目實戰如何将vendors_dll.js插入html?

業務代碼打包結果:

Webpack dll優化實戰DLL是什麼,用它來幹啥?前端應用場景項目實戰如何将vendors_dll.js插入html?

二者相加,打封包件體積與使用DllReferencePlugin前相仿。打包時間縮減了兩秒,在不增加打封包件體積的前提下,減少了打包時間。

如何将vendors_dll.js插入html?

  筆者在實踐過程中還發現一個問題,打包後的vendors_dll.js并沒有被HtmlWebpackPlugin所識别,導緻輸出的HTML檔案裡沒有這個資源,解決方法如下,在html模闆檔案的body添加一個script标簽,并且附帶内容模闆:

<body>
    <div id='main'></div>
    <script type="text/javascript" src="<%= htmlWebpackPlugin.options.vendor %>"></script>
</body>
           

然後在webpack檔案中作如下更改:

//  頭部引入打包好的檔案
const manifest = require('./dist/vendors-manifest.json'); 
......

    plugins: [
        ......
        new HtmlWebpackPlugin({
            filename: '../dist/index.html',
            template: './views/template.html',
            inject: 'body',
            //  添加vendor,替換html模闆中的内容
            vendor: './' + manifest.name + '.js' //manifest就是dll生成的json
        }),
        ......
    ]
           

最後在輸出的html中将會帶上我們的dll檔案。

上文中用到的例子:https://github.com/dianluyuanli-wp/chat