:::

Webpack引用方式對檔案大小的影響 / The Size of Webpack package files are different on Import from Files or Modules

1-Webpack_2.png

在Webpack中引用其他程式碼的模組解析,可以是來自於獨立的JavaScript檔案,也可以是來自於Node.js中已安裝的模組,可以使用require,也可以使用import。我很好奇到底這兩種引用方式對於最後產生的檔案大小有何影響,在這篇中我以引用獨立檔案或是已安裝的模組、require或是import這四種組合來引用 jQueryJSZipFileSaver 這三個模組,發現引用Node.js已安裝模組的方式產生的檔案較大,引用 獨立檔案所產生的檔案較小。而import產生的檔案比較大,require比較小。所以專案要選擇用require來引用獨立檔案會比較好嗎?我也不這樣認為。


對於Webpack打包的疑惑 / Question about module import in Webpack 

2019-0514-104954.png

我在「 閒聊Blogger範本程式碼的管理 」這篇講述到可以用Webpack將多個檔案打包成一個檔案,已達到節省HTTP請求所額外消耗的頻寬和時間。Wepback引用其他檔案的方式稱之為 模組解析(module resolution) ,可以透過import或require兩種方式來引用其他檔案。除了像傳統網頁一樣地引用個別的檔案之外,例如「import './some-module.js'」,在Webpack中也可以引用Node.js的模組,例如「import 'some-module'」,而後者是許多模組說明中推薦的作法。

引用 獨立檔案 / Import from files

2019-0514-105232.png

以「jQuery」這個前端開發時常用到的模組為例。它提供了已經打包好的單獨檔案,下載位置如下:

如果要引用這個檔案,在Webpack中的寫法是:

import $ from './jquery-3.4.1.min.js'
window.$ = $

第二行加上「window.$ = $」之後,在網頁的其他腳本中才能使用$。

引用模組 / Import from module

2019-0514-105310.png

此外,jQuery也有Node.js的模組。我們可以用npm來下載與安裝這個模組:

npm install jquery

安裝之後,在Webpack裡面要引用jQuery模組的寫法如下:

import $ from 'jquery'
window.$ = $

兩種引用模組的方式都很簡單,不過這對最後打包的檔案有什麼影響呢?讓我們來做個實驗吧。

測試打包檔案大小 / Test the size of bundle files from different import methods

2019-0514-110422.png

我開了一個GitHub儲存庫來執行Webpack的測試:

Webpack設定檔 / Webpack configuration

Webpack打包檔案的設定檔為webpack.config.js

我在設定檔中加入了 babel-loader ,讓ECMAScript 6的程式碼降級為ECMAScript 5,好讓瀏覽器能夠讀取。又使用 uglifyjs-webpack-plugin 來壓縮JavaScript程式碼。

最後以生產模式來執行Webpack,指令如下:

webpack --mode production

打包檔案 / Bundle files

在設定檔中,我總共會打包4個檔案:

這四個檔案都引用三種模組:

2019-0514-112629.png

打包後的檔案經過測試皆能順利使用這三種模組的功能,測試網頁的檔案位置也放在GitHub中了

檔案大小 / The size of bundle files

2019-0514-112949.png

最後打包檔案的檔案大小,由多至少排列如下:

在這種情況下,可以看到使用require比import所產生的檔案要小一點。而從獨立檔案引用所打包的檔案大小,又比從Node.js已安裝模組還要小得多。這似乎是表示我們應該準備獨立檔案來打包,而不要使用已安裝模組來打包,並且更應該使用require。

不過真的是這樣嗎?

討論 / Discussion

我個人覺得,這個簡單的測試所得到的答案,很難說是最終解答。以下我們從要不要從Node.js已安裝的模組引用,以及對於require和import的選擇來做個簡單的討論。

引用來源的選擇 / Difference of import source

要引用Node.js已安裝的模組呢?還是下載獨立檔案,然後引用獨立檔案呢?

從上面的實驗看下來,似乎是引用獨立檔案較能降低最後打包檔案的檔案大小。不過,因為這個實驗只是用簡單的單一檔案來做測試。在會引用大量模組的複雜專案中,又是另外一回事了。

實務上, 我認為引用獨立檔案的主要缺點在於 引用路徑的設定。要引用獨立檔案時,我們必須 指定絕對或相對路徑。大型專案中我們常常可能會在不同程式碼引用相同的模組。但是因為程式碼所在位置不同,如果要引用相同的獨立檔案,那就要特別注意程式碼與獨立檔案之間的相對位置。

舉例來說,如果程式碼跟要引用的獨立檔案在相同位置,則引用的寫法如下:

import './some-module.js'

但如果引用的獨立檔案是在其他資料夾中,那引用的寫法就會變得複雜許多,像是:

import '../libs/some-module.js'

相較之下, 引用已安裝模組的寫法皆相同,不需要考慮路徑位置:

import 'some-module'

從這個角度來看,似乎還是引用Node.js已安裝模組的話,會比較省工一些。

另一點是,有些模組仍然比較適合使用各別檔案來引用。例如semantic-ui有npm的安裝方式,不過實際上使用的時候似乎還是以個別獨立檔案來引用。另一方面summernote也一樣有npm,但使用的時候仍然是以獨立檔案個別引用。我們可以在HTML裡面用<link>跟<script>來引用,或是在Webpack裡面用import或require來引用,不過這就似乎不能用引用Node.js已安裝模組的寫法來引用,也就是「import 'summernote'」是沒有效果的。

似乎除此之外,兩者之間就沒有太多決定性的差異。我本來以為 Webpack的分塊功能splitChunks只能用在引用模組的時候,不過實際測試結果,對於獨立檔案也能夠順利分塊。關於Webpack分塊的設定與好處,請看 「 Webpack 4 教程 - 4. 使用SplitChunksPlugin插件进行代码分割 」 這篇。

require還是import / require or import

這個實驗中發現了require所打包的檔案大小會比import少,這是可以預期的事情。 我在Webpack的設定中使用了babel,它會將import語法轉換為require,又多了一層包裝,這可能是讓程式碼稍微變大的原因之一。

我對Node.js與Webpack的認識還不夠深,常常苦於require跟import之間的差異。實務上,使用require比較靈活,可以用傳統寫網頁的方式來撰寫,而且Webpack打包之後也比較小。import有許多限制,像是只能放在程式碼的最開頭,而且不能搭配邏輯來引用。require跟import在程式的運作上也有些微差異,簡單來說,import是更符合模組化、讓程式與程式之間更能完整隔離的作法。 許多模組對於如何引用它們有不同的見解。有些模組在範例上使用require,有些使用import。這可能跟模組開發的早晚時間有關係。

而且有時候要引用獨立檔案,特別是會用到jQuery的套件時,不論是使用require還是要用import,都得要稍微調整一下寫法才行。這部分我想另外寫一篇討論吧。

就目前為止, 似乎是遵循EMCAScript 6的import較受開發者青睞。雖然require出現得較早,許多早期教學也都以require作為範例,但開發者還是認為未來應該要迎合標準的import,早日捨棄CommonJS中的require。附帶一提,現在最新的版本是2018年6月提出的ECMAScript 2018 (ES2018),第9版。

關於這些不同的模組引用方式及其原理,可以參考「彻底搞清楚javascript中的require、import和export」這篇。

結語 / In closing

寫到這邊,總覺得我好像做了一次白工的感覺。

本來以為使用import跟引用Node.js模組會讓最後產生的檔案比較小,結果實際上是require跟引用獨立檔案比較小。不過幾經考量之後,又覺得還是繼續使用import來引用Node.js的已安裝模組比較方便。真是自打嘴巴。

不過,還有一件更殘酷的事實,請看下圖:

lib..Properties.png

如果只看import-from-file.js跟引用的三個獨立檔案,把這四個檔案的檔案大小相加,最後得到的檔案大小為183KB。顯然地又比前面打包的結果還要小。呃,若以最後打包大小來看的話,搞不好直接引用各別的檔案是最好的作法。

不過在經過上述討論,以及實際體驗過使用Webpack打包之後,就已經不太想回到拆開獨立檔案個別引用的開發方式了。


那麼這次對於Webpack引用方式如何影響最後打包檔案的大小的討論就到這邊了。你也是Webpack的使用者嗎?你是如何引用模組呢?有什麼特別值得注意的地方,可以跟大家分享嗎?歡迎在下面的留言處跟我們分享你的想法。如果你覺得我這篇寫的不錯的話,請幫我在AddThis分享工具按讚、將這篇分享到Facebook等社群媒體吧!感謝你的耐心閱讀,讓我們下一篇見。