濮阳杆衣贸易有限公司

主頁(yè) > 知識(shí)庫(kù) > 加速Webpack構(gòu)建技巧總結(jié)

加速Webpack構(gòu)建技巧總結(jié)

熱門(mén)標(biāo)簽:福州外呼系統(tǒng)中間件 威海人工智能電銷機(jī)器人系統(tǒng) 上海電銷卡外呼系統(tǒng)廠家 德州外呼系統(tǒng)排名 機(jī)器人電銷有什么用 貴州全自動(dòng)外呼系統(tǒng)廠家 好看的地圖標(biāo)注圖標(biāo)下載 地圖標(biāo)注員怎么去做 百度地圖標(biāo)注備注

Web 應(yīng)用日益復(fù)雜,相關(guān)開(kāi)發(fā)技術(shù)也百花齊放,這對(duì)前端構(gòu)建工具提出了更高的要求。 Webpack 從眾多構(gòu)建工具中脫穎而出成為目前最流行的構(gòu)建工具,幾乎成為目前前端開(kāi)發(fā)里的必備工具之一。 大多數(shù)人在使用 Webpack 的過(guò)程中都會(huì)遇到構(gòu)建速度慢的問(wèn)題,在項(xiàng)目大時(shí)顯得尤為突出,這極大的影響了我們的開(kāi)發(fā)體驗(yàn),降低了我們的開(kāi)發(fā)效率。

本文將傳授你一些加速 Webpack 構(gòu)建的技巧,下面來(lái)一一介紹。

通過(guò)多進(jìn)程并行處理

由于有大量文件需要解析和處理,構(gòu)建是文件讀寫(xiě)和計(jì)算密集型的操作,特別是當(dāng)文件數(shù)量變多后,Webpack 構(gòu)建慢的問(wèn)題會(huì)顯得嚴(yán)重。 運(yùn)行在 Node.js 之上的 Webpack 是單線程模型的,也就是說(shuō) Webpack 需要處理的任務(wù)需要一件件挨著做,不能多個(gè)事情一起做。

文件讀寫(xiě)和計(jì)算操作是無(wú)法避免的,那能不能讓 Webpack 同一時(shí)刻處理多個(gè)任務(wù),發(fā)揮多核 CPU 電腦的威力,以提升構(gòu)建速度呢?

使用 HappyPack

HappyPack 就能讓 Webpack 做到上面拋出的問(wèn)題,它把任務(wù)分解給多個(gè)子進(jìn)程去并發(fā)的執(zhí)行,子進(jìn)程處理完后再把結(jié)果發(fā)送給主進(jìn)程。

接入 HappyPack 的相關(guān)代碼如下:

const path = require('path');
  const ExtractTextPlugin = require('extract-text-webpack-plugin');
  const HappyPack = require('happypack');
  module.exports = { 
    module: { 
      rules: [ 
        {  test: /\.js$/, 
          // 把對(duì) .js 文件的處理轉(zhuǎn)交給 id 為 babel 的 HappyPack 實(shí)例 
          use:['happypack/loader?id=babel'], 
          // 排除 node_modules 目錄下的文件,node_modules目錄下的文件都是采用的 ES5 語(yǔ)法,沒(méi)必要再通過(guò) Babel 去轉(zhuǎn)換 
          exclude: path.resolve(__dirname, 'node_modules'),
         }, 
        { 
          // 把對(duì) .css 文件的處理轉(zhuǎn)交給 id 為 css 的 HappyPack 實(shí)例
           test: /\.css$/, 
           use:ExtractTextPlugin.extract({ 
            use: ['happypack/loader?id=css'],
       }), 
    }, 
] },
  plugins: [ 
    new HappyPack({ 
      // 用唯一的標(biāo)識(shí)符 id 來(lái)代表當(dāng)前的HappyPack 是用來(lái)處理一類特定的文件 
    id: 'babel', 
      // 如何處理 .js 文件,用法和 Loader配置中一樣 
    loaders: ['babel-loader?cacheDirectory'],
   }),
    new HappyPack({ 
        id: 'css', 
          // 如何處理 .css 文件,用法和Loader 配置中一樣 
        loaders: ['css-loader'], }), 
        new ExtractTextPlugin({ 
          filename: `[name].css`, 
      }), 
    ],
  };

以上代碼有兩點(diǎn)重要的修改:

在 Loader 配置中,所有文件的處理都交給了 happypack/loader 去處理,使用緊跟其后的 querystring ?id=babel 去告訴 happypack/loader 去選擇哪個(gè) HappyPack 實(shí)例去處理文件。
在 Plugin 配置中,新增了兩個(gè) HappyPack 實(shí)例分別用于告訴 happypack/loader 去如何處理 .js 和 .css 文件。選項(xiàng)中的 id 屬性的值和上面 querystring 中的 ?id=babel 相對(duì)應(yīng),選項(xiàng)中的 loaders 屬性和 Loader 配置中一樣。
接入 HappyPack 后,你需要給項(xiàng)目安裝新的依賴:

npm i -D happypack

安裝成功后重新執(zhí)行構(gòu)建,你就會(huì)看到以下由 HappyPack 輸出的日志:

Happy[babel]: Version: 4.0.0-beta.5. Threads: 3
Happy[babel]: All set; signaling webpack to proceed.Happy[css]: Version: 4.0.0-beta.5. Threads: 3Happy[css]: All set; signaling webpack to proceed.

說(shuō)明你的 HappyPack 配置生效了,并且可以得知 HappyPack 分別啟動(dòng)了3個(gè)子進(jìn)程去并行的處理任務(wù)。

在整個(gè) Webpack 構(gòu)建流程中,最耗時(shí)的流程可能就是 Loader 對(duì)文件的轉(zhuǎn)換操作了,因?yàn)橐D(zhuǎn)換的文件數(shù)據(jù)巨多,而且這些轉(zhuǎn)換操作都只能一個(gè)個(gè)挨著處理。 HappyPack 的核心原理就是把這部分任務(wù)分解到多個(gè)進(jìn)程去并行處理,從而減少了總的構(gòu)建時(shí)間。

從前面的使用中可以看出所有需要通過(guò) Loader 處理的文件都先交給了 happypack/loader 去處理,收集到了這些文件的處理權(quán)后 HappyPack 就好統(tǒng)一分配了。

每通過(guò) new HappyPack() 實(shí)例化一個(gè) HappyPack 其實(shí)就是告訴 HappyPack 核心調(diào)度器如何通過(guò)一系列 Loader 去轉(zhuǎn)換一類文件,并且可以指定如何給這類轉(zhuǎn)換操作分配子進(jìn)程。

核心調(diào)度器的邏輯代碼在主進(jìn)程中,也就是運(yùn)行著 Webpack 的進(jìn)程中,核心調(diào)度器會(huì)把一個(gè)個(gè)任務(wù)分配給當(dāng)前空閑的子進(jìn)程,子進(jìn)程處理完畢后把結(jié)果發(fā)送給核心調(diào)度器,它們之間的數(shù)據(jù)交換是通過(guò)進(jìn)程間通信 API 實(shí)現(xiàn)的。

核心調(diào)度器收到來(lái)自子進(jìn)程處理完畢的結(jié)果后會(huì)通知 Webpack 該文件處理完畢。

使用 ParallelUglifyPlugin

在使用 Webpack 構(gòu)建出用于發(fā)布到線上的代碼時(shí),都會(huì)有壓縮代碼這一流程。 最常見(jiàn)的 JavaScript 代碼壓縮工具是 UglifyJS,并且 Webpack 也內(nèi)置了它。

用過(guò) UglifyJS 的你一定會(huì)發(fā)現(xiàn)在構(gòu)建用于開(kāi)發(fā)環(huán)境的代碼時(shí)很快就能完成,但在構(gòu)建用于線上的代碼時(shí)構(gòu)建一直卡在一個(gè)時(shí)間點(diǎn)遲遲沒(méi)有反應(yīng),其實(shí)卡住的這個(gè)時(shí)候就是在進(jìn)行代碼壓縮。

由于壓縮 JavaScript 代碼需要先把代碼解析成用 Object 抽象表示的 AST 語(yǔ)法樹(shù),再去應(yīng)用各種規(guī)則分析和處理 AST,導(dǎo)致這個(gè)過(guò)程計(jì)算量巨大,耗時(shí)非常多。

為什么不把多進(jìn)程并行處理的思想也引入到代碼壓縮中呢?

ParallelUglifyPlugin 就做了這個(gè)事情。 當(dāng) Webpack 有多個(gè) JavaScript 文件需要輸出和壓縮時(shí),原本會(huì)使用 UglifyJS 去一個(gè)個(gè)挨著壓縮再輸出, 但是 ParallelUglifyPlugin 則會(huì)開(kāi)啟多個(gè)子進(jìn)程,把對(duì)多個(gè)文件的壓縮工作分配給多個(gè)子進(jìn)程去完成,每個(gè)子進(jìn)程其實(shí)還是通過(guò) UglifyJS 去壓縮代碼,但是變成了并行執(zhí)行。 所以 ParallelUglifyPlugin 能更快的完成對(duì)多個(gè)文件的壓縮工作。

使用 ParallelUglifyPlugin 也非常簡(jiǎn)單,把原來(lái) Webpack 配置文件中內(nèi)置的 UglifyJsPlugin 去掉后,再替換成 ParallelUglifyPlugin,相關(guān)代碼如下:

const path = require('path');
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
module.exports = { 
    plugins: [ 
      // 使用 ParallelUglifyPlugin 并行壓縮輸出的 JS 代碼
      new ParallelUglifyPlugin({ 
        // 傳遞給 UglifyJS 的參數(shù)
        uglifyJS: {
         }, 
      }), 
    ],
  };

接入 ParallelUglifyPlugin 后,項(xiàng)目需要安裝新的依賴:

npm i -D webpack-parallel-uglify-plugin

安裝成功后,重新執(zhí)行構(gòu)建你會(huì)發(fā)現(xiàn)速度變快了許多。如果設(shè)置 cacheDir 開(kāi)啟了緩存,在之后的構(gòu)建中會(huì)變的更快。

縮小文件搜索范圍

Webpack 啟動(dòng)后會(huì)從配置的 Entry 出發(fā),解析出文件中的導(dǎo)入語(yǔ)句,再遞歸的解析。 在遇到導(dǎo)入語(yǔ)句時(shí) Webpack 會(huì)做兩件事情:

根據(jù)導(dǎo)入語(yǔ)句去尋找對(duì)應(yīng)的要導(dǎo)入的文件。例如 require(‘react') 導(dǎo)入語(yǔ)句對(duì)應(yīng)的文件是 ./node_modules/react/react.js,而require(‘./util')導(dǎo)入語(yǔ)句 對(duì)應(yīng)的文件是 ./util.js。
根據(jù)找到的要導(dǎo)入文件的后綴,使用配置中的 Loader 去處理文件。例如使用 ES6 開(kāi)發(fā)的 JavaScript 文件需要使用 babel-loader 去處理。
以上兩件事情雖然對(duì)于處理一個(gè)文件非???,但是當(dāng)項(xiàng)目大了以后文件量會(huì)變的非常多,這時(shí)候構(gòu)建速度慢的問(wèn)題就會(huì)暴露出來(lái)。 雖然以上兩件事情無(wú)法避免,但需要盡量減少以上兩件事情的發(fā)生,以提高速度。

接下來(lái)一一介紹可以優(yōu)化它們的途徑。

縮小 resolve.modules 的范圍

Webpack的resolve.modules 用于配置 Webpack 去哪些目錄下尋找第三方模塊。

resolve.modules 的默認(rèn)值是 [‘node_modules'],含義是先去當(dāng)前目錄下的 ./node_modules 目錄下去找想找的模塊,如果沒(méi)找到就去上一級(jí)目錄 ../node_modules 中找,再?zèng)]有就去 ../../node_modules 中找,以此類推,這和 Node.js 的模塊尋找機(jī)制很相似。

當(dāng)安裝的第三方模塊都放在項(xiàng)目根目錄下的 ./node_modules 目錄下時(shí),沒(méi)有必要按照默認(rèn)的方式去一層層的尋找,可以指明存放第三方模塊的絕對(duì)路徑,以減少尋找,配置如下:

module.exports = {
      resolve: {
        // 使用絕對(duì)路徑指明第三方模塊存放的位置,以減少搜索步驟
        // 其中 __dirname 表示當(dāng)前工作目錄,也就是項(xiàng)目根目錄
        modules: [path.resolve(__dirname, 'node_modules')]
     },
};

縮小 Loader 的命中范圍

除此之外在使用 Loader 時(shí)可以通過(guò) test 、 include 、 exclude 三個(gè)配置項(xiàng)來(lái)命中 Loader 要應(yīng)用規(guī)則的文件。 為了盡可能少的讓文件被 Loader 處理,可以通過(guò) include 去命中只有哪些文件需要被處理。

以采用 ES6 的項(xiàng)目為例,在配置 babel-loader 時(shí),可以這樣:

module.exports = {
 module: {
  rules: [
   {
    // 如果項(xiàng)目源碼中只有 js 文件就不要寫(xiě)成 /\.jsx?$/,提升正則表達(dá)式性能
    test: /\.js$/,
    // babel-loader 支持緩存轉(zhuǎn)換出的結(jié)果,通過(guò) cacheDirectory 選項(xiàng)開(kāi)啟
    use: ['babel-loader?cacheDirectory'],
    // 只對(duì)項(xiàng)目根目錄下的 src 目錄中的文件采用 babel-loader
    include: path.resolve(__dirname, 'src'),
   },
  ]
 },
};

你可以適當(dāng)?shù)恼{(diào)整項(xiàng)目的目錄結(jié)構(gòu),以方便在配置 Loader 時(shí)通過(guò) include 去縮小命中范圍。

縮小 resolve.extensions 的數(shù)量
在導(dǎo)入語(yǔ)句沒(méi)帶文件后綴時(shí),Webpack 會(huì)自動(dòng)帶上后綴后去嘗試詢問(wèn)文件是否存在。 Webpack 配置中的 resolve.extensions 用于配置在嘗試過(guò)程中用到的后綴列表,默認(rèn)是:

extensions: ['.js', '.json']

也就是說(shuō)當(dāng)遇到 require(‘./data') 這樣的導(dǎo)入語(yǔ)句時(shí),Webpack 會(huì)先去尋找 ./data.js 文件,如果該文件不存在就去尋找 ./data.json 文件,如果還是找不到就報(bào)錯(cuò)。

如果這個(gè)列表越長(zhǎng),或者正確的后綴在越后面,就會(huì)造成嘗試的次數(shù)越多,所以 resolve.extensions 的配置也會(huì)影響到構(gòu)建的性能。 在配置 resolve.extensions 時(shí)你需要遵守以下幾點(diǎn),以做到盡可能的優(yōu)化構(gòu)建性能:

后綴嘗試列表要盡可能的小,不要把項(xiàng)目中不可能存在的情況寫(xiě)到后綴嘗試列表中。
頻率出現(xiàn)最高的文件后綴要優(yōu)先放在最前面,以做到盡快的退出尋找過(guò)程。
在源碼中寫(xiě)導(dǎo)入語(yǔ)句時(shí),要盡可能的帶上后綴,從而可以避免尋找過(guò)程。例如在你確定的情況下把 require(‘./data') 寫(xiě)成 require(‘./data.json')。
相關(guān) Webpack 配置如下:

module.exports = {
 resolve: {
  // 盡可能的減少后綴嘗試的可能性
  extensions: ['js'],
 },
};

縮小 resolve.mainFields 的數(shù)量

Webpack 配置中的 resolve.mainFields 用于配置第三方模塊使用哪個(gè)入口文件。

安裝的第三方模塊中都會(huì)有一個(gè) package.json 文件用于描述這個(gè)模塊的屬性,其中有些字段用于描述入口文件在哪里,resolve.mainFields 用于配置采用哪個(gè)字段作為入口文件的描述。

可以存在多個(gè)字段描述入口文件的原因是因?yàn)橛行┠K可以同時(shí)用在多個(gè)環(huán)境中,針對(duì)不同的運(yùn)行環(huán)境需要使用不同的代碼。 以 isomorphic-fetchfetch API 為例,它是 的一個(gè)實(shí)現(xiàn),但可同時(shí)用于瀏覽器和 Node.js 環(huán)境。

為了減少搜索步驟,在你明確第三方模塊的入口文件描述字段時(shí),你可以把它設(shè)置的盡量少。 由于大多數(shù)第三方模塊都采用 main 字段去描述入口文件的位置,可以這樣配置 Webpack:

module.exports = {
 resolve: {
  // 只采用 main 字段作為入口文件描述字段,以減少搜索步驟
  mainFields: ['main'],
 },
};

使用本方法優(yōu)化時(shí),你需要考慮到所有運(yùn)行時(shí)依賴的第三方模塊的入口文件描述字段,就算有一個(gè)模塊搞錯(cuò)了都可能會(huì)造成構(gòu)建出的代碼無(wú)法正常運(yùn)行。

善用現(xiàn)存的文件

通過(guò) module.noParse 忽略文件

Webpack 配置中的 module.noParse 配置項(xiàng)可以讓 Webpack 忽略對(duì)部分沒(méi)采用模塊化的文件的遞歸解析處理,這樣做的好處是能提高構(gòu)建性能。 原因是一些庫(kù),例如 jQuery 、ChartJS, 它們龐大又沒(méi)有采用模塊化標(biāo)準(zhǔn),讓 Webpack 去解析這些文件耗時(shí)又沒(méi)有意義。

在上面的 優(yōu)化 resolve.alias 配置 中講到單獨(dú)完整的 react.min.js 文件就沒(méi)有采用模塊化,讓我們來(lái)通過(guò)配置 module.noParse 忽略對(duì) react.min.js 文件的遞歸解析處理, 相關(guān) Webpack 配置如下:

module.exports = {
 module: {
  // 獨(dú)完整的 `react.min.js` 文件就沒(méi)有采用模塊化,忽略對(duì) `react.min.js` 文件的遞歸解析處理
  noParse: [/react\.min\.js$/],
 },
};

注意被忽略掉的文件里不應(yīng)該包含 import 、 require 、 define 等模塊化語(yǔ)句,不然會(huì)導(dǎo)致構(gòu)建出的代碼中包含無(wú)法在瀏覽器環(huán)境下執(zhí)行的模塊化語(yǔ)句。

通過(guò) resolve.alias 映射文件

Webpack 配置中的 resolve.alias 配置項(xiàng)通過(guò)別名來(lái)把原導(dǎo)入路徑映射成一個(gè)新的導(dǎo)入路徑。

在實(shí)戰(zhàn)項(xiàng)目中經(jīng)常會(huì)依賴一些龐大的第三方模塊,以 React 庫(kù)為例,庫(kù)中包含兩套代碼:

一套是采用 CommonJS 規(guī)范的模塊化代碼,這些文件都放在 lib 目錄下,以 package.json 中指定的入口文件 react.js 為模塊的入口。
一套是把 React 所有相關(guān)的代碼打包好的完整代碼放到一個(gè)單獨(dú)的文件中,這些代碼沒(méi)有采用模塊化可以直接執(zhí)行。其中 dist/react.js 是用于開(kāi)發(fā)環(huán)境,里面包含檢查和警告的代碼。dist/react.min.js 是用于線上環(huán)境,被最小化了。
默認(rèn)情況下 Webpack 會(huì)從入口文件 ./node_modules/react/react.js 開(kāi)始遞歸的解析和處理依賴的幾十個(gè)文件,這會(huì)時(shí)一個(gè)耗時(shí)的操作。 通過(guò)配置 resolve.alias 可以讓 Webpack 在處理 React 庫(kù)時(shí),直接使用單獨(dú)完整的 react.min.js 文件,從而跳過(guò)耗時(shí)的遞歸解析操作。

相關(guān) Webpack 配置如下:

module.exports = {
 resolve: {
  // 使用 alias 把導(dǎo)入 react 的語(yǔ)句換成直接使用單獨(dú)完整的 react.min.js 文件,
  // 減少耗時(shí)的遞歸解析操作
  alias: {
   'react': path.resolve(__dirname, './node_modules/react/dist/react.min.js'),
  }
 },
};

除了 React 庫(kù)外,大多數(shù)庫(kù)發(fā)布到 Npm 倉(cāng)庫(kù)中時(shí)都會(huì)包含打包好的完整文件,對(duì)于這些庫(kù)你也可以對(duì)它們配置 alias。

但是對(duì)于有些庫(kù)使用本優(yōu)化方法后會(huì)影響到后面要講的使用 Tree-Shaking 去除無(wú)效代碼的優(yōu)化,因?yàn)榇虬玫耐暾募杏胁糠执a你的項(xiàng)目可能永遠(yuǎn)用不上。 一般對(duì)整體性比較強(qiáng)的庫(kù)采用本方法優(yōu)化,因?yàn)橥暾募械拇a是一個(gè)整體,每一行都是不可或缺的。 但是對(duì)于一些工具類的庫(kù),例如 lodash,你的項(xiàng)目可能只用到了其中幾個(gè)工具函數(shù),你就不能使用本方法去優(yōu)化,因?yàn)檫@會(huì)導(dǎo)致你的輸出代碼中包含很多永遠(yuǎn)不會(huì)執(zhí)行的代碼。

使用 DllPlugin

在介紹 DllPlugin 前先給大家介紹下 DLL。 用過(guò) Windows 系統(tǒng)的人應(yīng)該會(huì)經(jīng)常看到以 .dll 為后綴的文件,這些文件稱為動(dòng)態(tài)鏈接庫(kù),在一個(gè)動(dòng)態(tài)鏈接庫(kù)中可以包含給其他模塊調(diào)用的函數(shù)和數(shù)據(jù)。

要給 Web 項(xiàng)目構(gòu)建接入動(dòng)態(tài)鏈接庫(kù)的思想,需要完成以下事情:

把網(wǎng)頁(yè)依賴的基礎(chǔ)模塊抽離出來(lái),打包到一個(gè)個(gè)單獨(dú)的動(dòng)態(tài)鏈接庫(kù)中去。一個(gè)動(dòng)態(tài)鏈接庫(kù)中可以包含多個(gè)模塊。
當(dāng)需要導(dǎo)入的模塊存在于某個(gè)動(dòng)態(tài)鏈接庫(kù)中時(shí),這個(gè)模塊不能再次被打包,而是去動(dòng)態(tài)鏈接庫(kù)中獲取。
頁(yè)面依賴的所有動(dòng)態(tài)鏈接庫(kù)需要被加載。
為什么給 Web 項(xiàng)目構(gòu)建接入動(dòng)態(tài)鏈接庫(kù)的思想后,會(huì)大大提升構(gòu)建速度呢? 原因在于包含大量復(fù)用模塊的動(dòng)態(tài)鏈接庫(kù)只需要編譯一次,在之后的構(gòu)建過(guò)程中被動(dòng)態(tài)鏈接庫(kù)包含的模塊將不會(huì)在重新編譯,而是直接使用動(dòng)態(tài)鏈接庫(kù)中的代碼。 由于動(dòng)態(tài)鏈接庫(kù)中大多數(shù)包含的是常用的第三方模塊,例如 react、react-dom,只要不升級(jí)這些模塊的版本,動(dòng)態(tài)鏈接庫(kù)就不用重新編譯。

接入 Webpack

Webpack 已經(jīng)內(nèi)置了對(duì)動(dòng)態(tài)鏈接庫(kù)的支持,需要通過(guò)2個(gè)內(nèi)置的插件接入,它們分別是:

DllPlugin 插件:用于打包出一個(gè)個(gè)單獨(dú)的動(dòng)態(tài)鏈接庫(kù)文件。
DllReferencePlugin 插件:用于在主要配置文件中去引入 DllPlugin 插件打包好的動(dòng)態(tài)鏈接庫(kù)文件。
下面以基本的 React 項(xiàng)目為例,為其接入 DllPlugin,在開(kāi)始前先來(lái)看下最終構(gòu)建出的目錄結(jié)構(gòu):

├── main.js
├── polyfill.dll.js
├── polyfill.manifest.json
├── react.dll.js
└── react.manifest.json

其中包含兩個(gè)動(dòng)態(tài)鏈接庫(kù)文件,分別是:

polyfill.dll.js 里面包含項(xiàng)目所有依賴的 polyfill,例如 Promise、fetch 等 API。
react.dll.js 里面包含 React 的基礎(chǔ)運(yùn)行環(huán)境,也就是 react 和 react-dom 模塊。
以 react.dll.js 文件為例,其文件內(nèi)容大致如下:

var _dll_react = (function(modules) {
 // ... 此處省略 webpackBootstrap 函數(shù)代碼
}([
 function(module, exports, __webpack_require__) {
  // 模塊 ID 為 0 的模塊對(duì)應(yīng)的代碼
 }
 // ... 此處省略剩下的模塊對(duì)應(yīng)的代碼 
]));

可見(jiàn)一個(gè)動(dòng)態(tài)鏈接庫(kù)文件中包含了大量模塊的代碼,這些模塊存放在一個(gè)數(shù)組里,用數(shù)組的索引號(hào)作為 ID。 并且還通過(guò) _dll_react 變量把自己暴露在了全局中,也就是可以通過(guò) window._dll_react 可以訪問(wèn)到它里面包含的模塊。

其中 polyfill.manifest.json 和 react.manifest.json 文件也是由 DllPlugin 生成,用于描述動(dòng)態(tài)鏈接庫(kù)文件中包含哪些模塊, 以 react.manifest.json 文件為例,其文件內(nèi)容大致如下:

{
 // 描述該動(dòng)態(tài)鏈接庫(kù)文件暴露在全局的變量名稱
 "name": "_dll_react",
 "content": {
  "./node_modules/process/browser.js": {
   "id": 0,
   "meta": {}
  },
  // ... 此處省略部分模塊
 }
}

可見(jiàn) manifest.json 文件清楚地描述了與其對(duì)應(yīng)的 dll.js 文件中包含了哪些模塊,以及每個(gè)模塊的路徑和 ID。

main.js 文件是編譯出來(lái)的執(zhí)行入口文件,當(dāng)遇到其依賴的模塊在 dll.js 文件中時(shí),會(huì)直接通過(guò) dll.js 文件暴露出的全局變量去獲取打包在 dll.js 文件的模塊。 所以在 index.html 文件中需要把依賴的兩個(gè) dll.js 文件給加載進(jìn)去,index.html 內(nèi)容如下:

<!--導(dǎo)入依賴的動(dòng)態(tài)鏈接庫(kù)文件-->
<script src="./dist/polyfill.dll.js"></script>
<script src="./dist/react.dll.js"></script>
<!--導(dǎo)入執(zhí)行入口文件-->
<script src="./dist/main.js"></script>

以上就是所有接入 DllPlugin 后最終編譯出來(lái)的代碼,接下來(lái)教你如何實(shí)現(xiàn)。

構(gòu)建出動(dòng)態(tài)鏈接庫(kù)文件
構(gòu)建輸出的以下這四個(gè)文件

├── polyfill.dll.js
├── polyfill.manifest.json
├── react.dll.js
└── react.manifest.json

和以下這一個(gè)文件

├── main.js

是由兩份不同的構(gòu)建分別輸出的。

與動(dòng)態(tài)鏈接庫(kù)相關(guān)的文件需要由一個(gè)獨(dú)立的構(gòu)建輸出,用于給主構(gòu)建使用。新建一個(gè) Webpack 配置文件 webpack_dll.config.js 專門(mén)用于構(gòu)建它們,文件內(nèi)容如下:

const path = require('path');
const DllPlugin = require('webpack/lib/DllPlugin');
 
module.exports = {
 // JS 執(zhí)行入口文件
 entry: {
  // 把 React 相關(guān)模塊的放到一個(gè)單獨(dú)的動(dòng)態(tài)鏈接庫(kù)
  react: ['react', 'react-dom'],
  // 把項(xiàng)目需要所有的 polyfill 放到一個(gè)單獨(dú)的動(dòng)態(tài)鏈接庫(kù)
  polyfill: ['core-js/fn/object/assign', 'core-js/fn/promise', 'whatwg-fetch'],
 },
 output: {
  // 輸出的動(dòng)態(tài)鏈接庫(kù)的文件名稱,[name] 代表當(dāng)前動(dòng)態(tài)鏈接庫(kù)的名稱,
  // 也就是 entry 中配置的 react 和 polyfill
  filename: '[name].dll.js',
  // 輸出的文件都放到 dist 目錄下
  path: path.resolve(__dirname, 'dist'),
  // 存放動(dòng)態(tài)鏈接庫(kù)的全局變量名稱,例如對(duì)應(yīng) react 來(lái)說(shuō)就是 _dll_react
  // 之所以在前面加上 _dll_ 是為了防止全局變量沖突
  library: '_dll_[name]',
 },
 plugins: [
  // 接入 DllPlugin
  new DllPlugin({
   // 動(dòng)態(tài)鏈接庫(kù)的全局變量名稱,需要和 output.library 中保持一致
   // 該字段的值也就是輸出的 manifest.json 文件 中 name 字段的值
   // 例如 react.manifest.json 中就有 "name": "_dll_react"
   name: '_dll_[name]',
   // 描述動(dòng)態(tài)鏈接庫(kù)的 manifest.json 文件輸出時(shí)的文件名稱
   path: path.join(__dirname, 'dist', '[name].manifest.json'),
  }),
 ],
};

使用動(dòng)態(tài)鏈接庫(kù)文件

構(gòu)建出的動(dòng)態(tài)鏈接庫(kù)文件用于在其它地方使用,在這里也就是給執(zhí)行入口使用。

用于輸出 main.js 的主 Webpack 配置文件內(nèi)容如下:

const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');
 
module.exports = {
 plugins: [
  // 告訴 Webpack 使用了哪些動(dòng)態(tài)鏈接庫(kù)
  new DllReferencePlugin({
   // 描述 react 動(dòng)態(tài)鏈接庫(kù)的文件內(nèi)容
   manifest: require('./dist/react.manifest.json'),
  }),
  new DllReferencePlugin({
   // 描述 polyfill 動(dòng)態(tài)鏈接庫(kù)的文件內(nèi)容
   manifest: require('./dist/polyfill.manifest.json'),
  }),
 ],
 devtool: 'source-map'
};

注意:在 webpack_dll.config.js 文件中,DllPlugin 中的 name 參數(shù)必須和 output.library 中保持一致。 原因在于 DllPlugin 中的 name 參數(shù)會(huì)影響輸出的 manifest.json 文件中 name 字段的值, 而在 webpack.config.js 文件中 DllReferencePlugin 會(huì)去 manifest.json 文件讀取 name 字段的值, 把值的內(nèi)容作為在從全局變量中獲取動(dòng)態(tài)鏈接庫(kù)中內(nèi)容時(shí)的全局變量名。

執(zhí)行構(gòu)建

在修改好以上兩個(gè) Webpack 配置文件后,需要重新執(zhí)行構(gòu)建。 重新執(zhí)行構(gòu)建時(shí)要注意的是需要先把動(dòng)態(tài)鏈接庫(kù)相關(guān)的文件編譯出來(lái),因?yàn)橹?Webpack 配置文件中定義的 DllReferencePlugin 依賴這些文件。

執(zhí)行構(gòu)建時(shí)流程如下:

如果動(dòng)態(tài)鏈接庫(kù)相關(guān)的文件還沒(méi)有編譯出來(lái),就需要先把它們編譯出來(lái)。方法是執(zhí)行 webpack –config webpack_dll.config.js 命令。

在確保動(dòng)態(tài)鏈接庫(kù)存在的前提下,才能正常的編譯出入口執(zhí)行文件。方法是執(zhí)行 webpack 命令。這時(shí)你會(huì)發(fā)現(xiàn)構(gòu)建速度有了非常大的提升。

相信給你的項(xiàng)目加上以上優(yōu)化方法后,構(gòu)建速度會(huì)大大提高,趕快去試試把!

標(biāo)簽:葫蘆島 南陽(yáng) 白城 撫州 邵陽(yáng) 泉州 岳陽(yáng) 南陽(yáng)

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《加速Webpack構(gòu)建技巧總結(jié)》,本文關(guān)鍵詞  加速,Webpack,構(gòu)建,技巧,總結(jié),;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《加速Webpack構(gòu)建技巧總結(jié)》相關(guān)的同類信息!
  • 本頁(yè)收集關(guān)于加速Webpack構(gòu)建技巧總結(jié)的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    宁波市| 福鼎市| 神池县| 桦南县| 鄂尔多斯市| 方城县| 调兵山市| 昌图县| 洛隆县| 唐河县| 松阳县| 阿合奇县| 沁阳市| 织金县| 黎城县| 连州市| 尖扎县| 长泰县| 富宁县| 乐清市| 江山市| 宜春市| 沾化县| 安福县| 常州市| 岳阳县| 岳西县| 马鞍山市| 洛川县| 成武县| 政和县| 吕梁市| 大石桥市| 和硕县| 集贤县| 望谟县| 法库县| 雷波县| 江达县| 巴马| 莱西市|