Webpack構建項目進一步優化


Step14.Webpack構建項目進一步優化

webpack dll VS external

webpack在打包后,生成的文件主要分為三種類型:

* 業務代碼

* 外部依賴庫

* webpack runtime

 

webpack中的dll和external在本質上其實是解決的同一個問題:避免將某些外部依賴庫打包進我們的業務代碼,而是在運行時提供這些依賴。

一方面實現了代碼拆分,以及依賴的復用,另一方面提升構建速度.

這兩種方案應該是各有各的優劣,分別適用於不同的環境。

(1) dllexternals的區別

dll 符合前端模塊化的要求

webpack配置上稍微復雜一些,需要預打包所需的dll資源,並在構建時配置相應的plugin,

使用dll的前提是,這些外部依賴一般不需要發生變更。所以,如果某天發生了變更,那就需要將項目重新構建,違背了dll的使用前提,必然要作出相應的犧牲。

external不太符合前端的模塊化思想,所需要的外部庫需要在瀏覽器全局環境下可訪問

外部庫升級的話,如果兼容之前的API,不需要項目重新構建

 

webpack配置上稍微簡單些,但是同樣需要將所需的外部庫打包為所需要的格式,並在運行態下引用

相比較而言的話,dll比external應該更加智能一些,主要體現在模塊的引用和打包上。比如說如下方式去引用了react中的一個方法:

 

import AA from 'react/lib/createClass'

 

如果采用dll的方式,是不會造成重復打包的,他會將引用直接指向dll。但是如果使用external的話,則會react中的部分代碼打包進來。

 

(2) externals

防止將某些 import 的包(package)打包 bundle 中,而是在運行時(runtime)再去從外部獲取這些擴展依賴(external dependencies)

例如,從 CDN 引入 jQuery,而不是把它打包

<script src="https://code.jquery.com/jquery-3.1.0.js""></script>

module.exports = {

  //...

  externals: {

    jquery: 'jQuery'

  }};

 

這樣就剝離了那些不需要改動的依賴模塊,換句話,下面展示的代碼還可以正常運行:

import $ from 'jquery';

$('.my-element').animate(/* ... */);

 

DLLPlugin 和 DLLReferencePlugin的使用

DLLPlugin為什么會出現?

 

在使用webpack進行打包時候,對於依賴的第三方庫,比如vuevuex等這些不會修改的依賴,我們可以讓它和我們自己編寫的代碼分開打包,這樣做的好處是每次更改我本地代碼的文件的時候,webpack只需要打包我項目本身的文件代碼,而不會再去編譯第三方庫,那么第三方庫在第一次打包的時候只打包一次,以后只要我們不升級第三方包的時候,那么webpack就不會對這些庫去打包,這樣的可以快速的提高打包的速度。因此為了解決這個問題,DllPlugin DllReferencePlugin插件就產生了。

 

https://www.webpackjs.com/plugins/dll-plugin/#dllplugin

 

DLLPlugin

 

 

DLLPlugin 它能把第三方庫代碼分離開,並且每次文件更改的時候,它只會打包該項目自身的代碼。所以打包速度會更快。

DLLPlugin 這個插件是在一個額外獨立的webpack設置中創建一個只有dll的bundle,也就是說我們在項目根目錄下除了有webpack.config.js,還會新建一個webpack.dll.config.js文件。webpack.dll.config.js作用是把所有的第三方庫依賴打包到一個bundle的dll文件里面,還會生成一個名為 manifest.json文件。
manifest.json的作用是用來讓 DllReferencePlugin 映射到相關的依賴上去的。

 

DllReferencePlugin 這個插件是在webpack.config.js中使用的,該插件的作用是把剛剛在webpack.dll.config.js中打包生成的dll文件引用到需要的預編譯的依賴上來。什么意思呢?就是說在webpack.dll.config.js中打包后比如會生成 vendor.dll.js文件和vendor-manifest.json文件,vendor.dll.js文件包含所有的第三方庫文件,vendor-manifest.json文件會包含所有庫代碼的一個索引,當在使用webpack.config.js文件打包DllReferencePlugin插件的時候,會使用DllReferencePlugin插件讀取vendor-manifest.json文件,看看是否有該第三方庫。vendor-manifest.json文件就是有一個第三方庫的一個映射而已。

 

所以說 第一次使用 webpack.dll.config.js 文件會對第三方庫打包,打包完成后就不會再打包它了,然后每次運行 webpack.config.js文件的時候,都會打包項目中本身的文件代碼,當需要使用第三方依賴的時候,會使用 DllReferencePlugin插件去讀取第三方依賴庫。所以說它的打包速度會得到一個很大的提升。

 

 

DLLPlugin 和 DLLReferencePlugin 用某種方法實現了拆分 bundles,同時還大大提升了構建的速度。

(1) 配置webpack.dll.config.js:

var path = require("path");

var webpack = require("webpack");

var SRC_PATH = path.resolve(__dirname,'./src');

module.exports = {

  // 要打包的模塊的數組

  entry: {

    vendor: ['vue/dist/vue.esm.js','vue-router']

  },

  output: {

    path: path.join(__dirname, './static/dll), // 打包后文件輸出的位置

    filename: '[name].dll.js',// vendor.dll.js中暴露出的全局變量名。

    library: '[name]_library' // webpack.DllPlugin中的`name: '[name]_library',`保持一致。

  },

  plugins: [

    new webpack.DllPlugin({

      path: path.join(SRC_PATH,'./static/dll/[name]-manifest.json'),

      name: '[name]_library', 

      context: __dirname

    }),

  ]

};

 

DllPlugin 插件有三個配置項參數如下:
context(可選): manifest文件中請求的上下文,默認為該webpack文件上下文。
name: 公開的dll函數的名稱,和 output.library保持一致。
path: manifest.json 生成文件的位置和文件名稱。

 

上面就是webpack.dll.conf.js的主要配置。執行之后會在static文件夾(在vue-cli生成的項目中用於存放不需要webpack構建的靜態文件【@vue/cli中的目錄名為Public】)下生成兩個文件夾(lib文件夾和mainfest文件夾)。其中lib下的文件為我們已經打包好的組件庫,mainfest下的文件在引入項目時有用(是一個JSON文件)。

 

package.jsonscripts里加上:

"dll": "webpack --config  webpack.dll.config.js",

 

運行npm run dll static下生成dll文件夾,dll文件夾里包括js文件和json文件

(2) webpack.base.conf.js里加上:

// 添加DllReferencePlugin插件

  plugins: [

    new webpack.DllReferencePlugin({

      context: __dirname,

      manifest: require('./static/dll/vendor-manifest.json')

    })

  ],

 

DllReferencePlugin項的參數有如下:

context: manifest文件中請求的上下文。
manifest: 編譯時的一個用於加載的JSON的manifest的絕對路徑。
mainfest: 請求到模塊id的映射(默認值為 manifest.content)
name: dll暴露的地方的名稱(默認值為manifest.name)
scope: dll中內容的前綴。
sourceType: dll是如何暴露的libraryTarget。

(3) index.html中引入vendor.dll.js:

由於動態鏈接庫我們一般只編譯一次,除非依賴的三方庫更新,之后就不用編譯,因此入口的 index.js 文件中不包含這些模塊,所以要在 index.html 中單獨引入。

 

① 第一種方式, 一種是根據打包的路徑手動添加

<div id="app"></div><script src="./static/js/vendor.dll.js"></script>

② 第二種方式,用add-asset-html-webpack-plugin或者html-webpack-include-assets-plugin插入到html中,簡單自動化

 

下面着重講下add-asset-html-webpack-plugin與html-webpack-include-assets-plugin插件的使用,項目中使用add-asset-html-webpack-plugin

安裝大同小異

npm安裝:

npm install add-asset-html-webpack-plugin -D

npm install html-webpack-include-assets-plugin -D

Yarn安裝:

yarn add add-asset-html-webpack-plugin -D

yarn add html-webpack-include-assets-plugin -D

 

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

const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
 
module.exports = {
    ...,
    plugins: [
        ...,
        # 給定的 JS  CSS 文件添加到 webpack 配置的文件中,並將其放入資源列表 html webpack插件注入到生成的 html 中。
        new AddAssetHtmlPlugin([
            {
                # 要添加到編譯中的文件的絕對路徑
                filepath: path.resolve(__dirname,'./static/dll/dll_vendor.js'),
                outputPath: 'dll',
                publicPath: 'dll',
                includeSourcemap: false
            }
        ])
    ]
}

html-webpack-include-assets-plugin的使用

 

 

const HtmlWebpackIncludeAssetsPlugin = require('html-webpack-include-assets-plugin');

 

module.exports = {

    ...,

    plugins: [

        ...,

        // 給定的 JS CSS 文件添加到 webpack 配置的文件中,並將其放入資源列表 html webpack插件注入到生成的 html 中。

    new HtmlWebpackIncludeAssetsPlugin(

          {

              assets: ['dll/asset.dll.js','dll/vendor.dll.js'],

              append: false

          }

       )

]

 

至此,配置之后的:
可以看到npm run build后的時間大幅度減少,在dist打包體積上也比之前的小。在項目優化中,可以很大程度上加快項目的構建速度和減少項目的打包體積。

使用 happypack 提升 Webpack 項目構建速度

提示:由於HappyPack file-loaderurl-loader 支持的不友好,所以不建議對該loader使用。

 

webpack打包哪一步最耗時?可能要數loader對文件的轉換操作了,我們前面說過,我們使用loader將文件轉換為我們需要的類型,文件數量巨大,webpack執行又是單線程的,轉換的操作只能一個一個的處理,不能多件事一起做。
我們需要Webpack 能同一時間處理多個任務,發揮多核 CPU 電腦的威力,HappyPack 就能讓 Webpack 做到這點,我們將需要通過loader處理的文件先交給happypack去處理,happypack 在收集到這些文件的處理權限后,統一分配CPU資源.

happypack工作原理

happypack 通過new HappyPack(),去實例化一個HappyPack對象,其實就是告訴Happypack核心調度器如何通過一系列loader去轉換一類文件,並且可以指定如何為這類轉換器作分配子進程。
核心調度器的邏輯代碼在主進程里,也就是運行webpack的進程中,核心調度器會將一個個任務分配給當前空閑的子進程,子進程處理完后會將結果發送給核心調度器,它們之間的數據交換是通過進程間的通訊API實現的。
核心調度器收到來自子進程處理完畢的結果后,會通知webpack該文件已經處理完畢

 

參考:https://www.qdtalk.com/2018/11/16/webpack4plugin-2/

安裝:

yarn add happypack --dev

使用:

// 引入 happypack

const HappyPack = require('happypack');

 

// 創建 happypack 共享進程池,其中包含 6 個子進程

const happyThreadPool = HappyPack.ThreadPool({ size: 6 });

 

module.exports = {

//省略部分配置
  module: {
    rules: [
      {
        test: /\.js$/,
        //把對.js 的文件處理交給id為happyBabel 的HappyPack 的實例執行
         use: 'happypack/loader?id=happyBabel',
        //排除node_modules 目錄下的文件,合理的使用排除可以事半功倍
        exclude: path.resolve(__dirname,'node_modules')
      },
    ]
  },
plugins: [
    new HappyPack({
        //用id來標識 happypack處理那里類文件
      id: 'happyBabel',
      //如何處理js文件  用法和loader 的配置一樣
      loaders: [{
        loader: 'babel-loader?cacheDirectory=true',
      }],
      //使用共享進程池中的自進程去處理任務

      threadPool: happyThreadPool,
      //允許 HappyPack 輸出日志,默認為true
      verbose: true,
    })
  ]
}

Loader 配置中,所有文件的處理都交給了 happypack/loader 去處理,使用緊跟其后的 querystring ?id=babel 去告訴 happypack/loader 去選擇哪個 HappyPack 實例去處理文件。

Plugin 配置中,新增了兩個 HappyPack 實例分別用於告訴 happypack/loader 去如何處理 .js 和 .css 文件。選項中的 id 屬性的值和上面 querystring 中的 ?id=babel 相對應,選項中的 loaders 屬性和 Loader 配置中一樣。

 

對應的參數

 

id:String   

用唯一的標識符 id 來代表當前的 HappyPack 是用來處理一類特定的文件.

loaders: Array   

用法和 webpack Loader 配置中一樣.

threads: Number  

代表開啟幾個子進程去處理這一類型的文件,默認是3個,類型必須是整數。

verbose: Boolean

是否允許 HappyPack 輸出日志,默認是 true。

threadPool: HappyThreadPool

代表共享進程池,即多個 HappyPack 實例都使用同一個共享進程池中的子進程去處理任務,以防止資源占用過多。

verboseWhenProfiling: Boolean

開啟webpack --profile ,仍然希望HappyPack產生輸出。

debug: Boolean

啟用debug 用於故障排查。默認 false。

 

https://blog.csdn.net/zgd826237710/article/details/88172290#_HappyPack_36

注意

注意,webpack4中的happypack要使用5.0.0版本,如果你是從webpack3升級到webpack4,記得升級happypack

上面的loader中出現一個陌生詞cacheDirectory:
cacheDirectory默認值為 false。
當有設置時,指定的目錄將用來緩存 loader 的執行結果。之后的 webpack 構建,將會嘗試讀取緩存,來避免在每次執行時,可能產生的、高性能消耗的 Babel 重新編譯過程(recompilation process)。如果設置了一個空值 (loader: ‘babel-loader?cacheDirectory’) 或者 true (loader: babel-loader?cacheDirectory=true),loader 將使用默認的緩存目錄 node_modules/.cache/babel-loader,如果在任何根目錄下都沒有找到 node_modules 目錄,將會降級回退到操作系統默認的臨時文件目錄。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM