當前位置:
首頁 > 最新 > webpack 從入門到工程實踐

webpack 從入門到工程實踐

本文來自作者張旺在 GitChat 上精彩分享

前言

本文較長,為了節省你的閱讀時間,在文前列寫作思路如下:

什麼是,它要解決的是什麼問題?

對的主要配置項進行分析,雖然不會涉及太多細節,但是期待在本節能讓我們知曉如果我們有什麼需求,我們該從哪些配置項著手修改?

分析的基礎配置文件。

分享一些自己工作中對的實踐。

本文的初衷是和你一起理清的使用邏輯,以便能更加容易的編寫及拓展自己項目所需的配置文件。

不過也得提前說明本文可能並不是一篇好的可以跟著操作的教程(想跟著一步步做的童鞋可以看官方示例(https://webpack.js.org/guides/)和webpack 入門,看這篇就夠了(http://www.jianshu.com/p/42e11515c10f)。

換個角度看待 webpack

近年來,前端技術蓬勃發展,我們想在更方便的實現, 社區就出現了,我們覺得原生的不夠好用,社區就提出了,,針對前端項目越來越強的模塊化開發需求,社區出現了,,等等方案。

遺憾的是,這些方案大多並不直接被瀏覽器支持,往往伴隨這些方案而生的還有另外一些,讓這些新技術應用於瀏覽器的方案,我們用來轉換下一代的,轉換;我們用各種工具轉換,為;

我們發現項目越來越複雜,代碼體積越來越大,又要開始尋找各種優化,壓縮,分割方案。前端工程化這個過程,真是讓我們大費精力。我們也大多是在尋找前端模塊化解決方案的過程中知曉了。

的確,的流行得益於野性生長的前端,其本質是一種前端模塊化打包解決方案,但是更重要的是它又是一個可以融合運用各種前端新技術的平台,明白的使用哲學後,只需要簡單的配置,我們就可以隨心所欲的在項目中使用使用等平台提供的眾多其它功能,只需通過一條命令由源碼構建最終可用文件。

可以不誇張的說為前端的工程化開發提供了一套相對容易和完整的解決方案。一些知名的腳手架工具,也大多基於(比如)。

好難!我第一次複製別人的配置文件到我的項目中,發現以自己僅有的JS知識完全看不懂時,也有這種感覺。

後來發現有這種感覺其實是因為自己看待的角度錯了,對大多數前端開發者而言,以往我們接觸的各種庫,要麼類似,通過符在前端項目中直接運行,所做的事情只在前端生效,要麼類似,在項目中直接後就可以使用,所做的事情只在後端生效。

的不同之處就在於,雖然我們的配置文件位於前端項目中,但實際上它卻運行於,之後的處理結果又供前端使用(也可能供node使用)。所以學習之前,我們轉變一下思維,從的角度來看,很多事情就會簡單起來。

我們對下圖一定不陌生,假設現在我們手中有一系列相互關聯的文件,,,,,我們一步步的看看為了把它們轉換為項目最終需要的,瀏覽器可識別的文件,都做了什麼。

對 webpack 主要配置項的分析

如果不去考究細節,我們大可把簡化理解為一個函數,配置文件則是其參數,傳入合理的參數後,運行函數就能得到我們想要的結果。

也只是一個打包工具,它可不是什麼智能,我們該從哪兒輸入文件,我們想把輸出結果放哪裡,輸出結果應該長什麼樣,它都不知道。而我們目前和函數交互的唯一方法就是通過參數,這就涉及到配置對象中兩個重要概念和了,因此,我們的配置對象至少具備以下結構:

入口配置 entry

理想狀態是,我們把所有自己編寫的文件都交給,讓它找明裡面的關係,進過一定處理後,給出最終我們想要的結果。

遺憾的是,也不會機械學習,我們手頭的一堆文件之間的關係是自己確定的,一般我們的項目都會存在一個或幾個主文件,其它的所有的文件(模塊)都直接或間接的鏈接到了這些文件。我們在項中需要填寫的就是這些主文件的信息。

不過我們也不要嫌棄笨,通過我們給的主文件路徑,通過分析它能構建最合適的依賴關係,這意味著只有用過的代碼才會被打包,比如我們在一個文件中寫了五個模塊,但是實際只用了其中一個,打包後的代碼只會包含引用過的模塊。

中很多地方的配置都有多種寫法,這也是其讓人疑惑的地方之一,很遺憾,我們的第一個配置對象就是如此。

可以是三種值:

字元串:如,字元串也可以是函數的返回值,如,單一入口佔位符值為(關於佔位符,稍後詳述);

數組形式,如,可以把數組中的多個文件打包轉換為一個;

對象形式,如果我們需要配置的是多頁應用,或者我們要抽離出指定的模塊做為公共代碼,就需要採用這種形式了,屬性名是佔位符的值,屬性值可以是上面的字元串和數組,如下:

好吧,千辛萬苦,我們在一堆各種類型的文件中找到了入口文件,這裡我們假設為,此時我們的配置對象如下:

依據入口文件來構建依賴體系,每個入口文件在打包完成後都具備其獨立的依賴圖譜,在此我們暫時稱這些由主入口配置生成的文件為主文件。

輸出配置 output

配置項作用於打包文件的輸出階段,其作用在於告知以何種方式輸出打包文件,關於,提供了眾多的可配置選項,我們簡單介紹下最常用的選項。

output基本配置項

我們都另存過文件,當我們另存一個文件時,我們需要確定另存的文件名和另存的路徑,將打包後的結果導出的過程就類似於此,此過程由配置項控制,其最基本配置包括和兩項。這兩項用以決定上述主文件的存儲行為。

不過我們程序的首頁往往不需用到某個主文件的所有代碼,實際開發中,我們常常使用一定方法對代碼進行分割,方便按需載入,提升體驗。這類不具備獨立依賴的文件,我們稱之為。的命名,在中對應項;

此外的項,用於控制打包文件的相對或者絕對引用路徑,配置不當往往造成在運行時找不到文件。

我們補充配置對象中的配置,如下:

上述代碼中用到了佔位符,我們對佔位符做統一解釋:

中常見的佔位符有多種,常見的如下:

:代表打包後文件的名稱,在或代碼中(之後會看到)確定;

:給塊分配的內部,如果你沒有隱藏,你能在打包後的命令行中看到;

:每次構建過程中,生成的唯一 hash 值;

: 依據於打包生成文件內容的 hash 值,內容不變,值不變;

: 資源擴展名,如,,等等;

output 其它配置

配置項生效於保存這個過程,除了上面的基本配置,如果你想對這個階段的打包文件進行更改,都可在此配置項中進行相關設置。

比如提供了眾多關於的屬性,讓我們對佔位符的值有更加精細的控制,如生成方式,使用的演算法,預設的長度等等;如屬性則允許我們設置文件的請求超時時間。

工具都是依賴於需求來使用的,如果你此階段有別的需求,可點擊更多配置尋找解決方案。

我們已經知道了中基本的輸入和輸出配置,但是對各模塊的處理過程,目前為止,對我們還是一個謎。考慮到執行於環境,其本身只能理解文件,而我們輸入的卻是一大堆不同格式的文件,毫無疑問,要做的第一件事情是對各類模塊進行處理,這就涉及到中第三個重要配置對象了—-。

對模塊的處理:module 的配置

使用時,我們常常聽說,對而言,所有的文件都是模塊,前文中我也常常混用模塊和文件,不過本質上模塊和文件還是不同的,里,文件可以當做模塊,而模塊卻不一定是一個獨立的文件。我們先看看內置支持的模塊類型:

(開始內置支持)。

CommonJS。

AMD和語句。

css/less/sass 中的。

樣式中的和html文件中的。

我們知道只能處理文件,我們的瀏覽器也可能不支持一些最新的js語法,基於此,我們需要對傳入的模塊進行一定的預處理,這就涉及到的又一核心概念 —-,使用,允許我們打包任何JS之外的靜態資源。

loader 的作用和基本用法

中,的配置主要在中進行,是一個數組,我們可以把每一項看做一個,每個主要做了以下兩件事:

識別文件類型,以確定具體處理該數據的,(屬性)。

使用相關對文件進行相應的操作轉換,(屬性)。

還記得前面我們說過,我們手頭的文件類型有,,,,嗎?我們看看在中該如何處理和轉換它們。

註:以下使用前需通過安裝:

這就是中的基本用法了,在數組中進行配置即可,是一個數組,裡面每一項(一個)表示以一定的規則匹配和處理某種或某幾種類型的文件。具體說來:

:表示匹配規則,它是一個正則表達式。

:表示針對匹配的文件將使用的處理,其值可以是字元串,數組和對象,當是對象形式時,我們可以使用等命令進行進一步的配置。

中的其它一些規則也大多圍繞匹配條件和應用結果展開,如和表示應該匹配或不應該匹配某資源;表示對該資源只應用第一個匹配的;則用於指定的種類。

loader 可以做什麼

除了上述的轉換編譯,通過,還允許我們實現以下功能:

轉換編譯:等。

處理樣式:等。

處理文件:等。

處理數據:等。

處理模板語言:等。

清理和測試:等。

關於各個loader更詳細的介紹,可點擊loaders查看。

module.noParse

關於,另一個常用的配置項為,通過它,我們在構建過程中可以忽略大型的 library 以提高構建效率。

我們來整理一下此階段,我們的配置對象代碼,如下:

進過這一階段的處理,我們的代碼其實已經可以輸出使用了。不過這樣的輸出可能還不能讓人滿意,我們想要抽離公共代碼;我們想統一修改所有代碼中的某些值;我們還想對代碼進行壓縮,去除所有的… , 總之這一階段的代碼還是存在很大的改進空間的,這就是的用武之地了。

plugins 的配置

稱為其,一切不能做的處理都可由來做。此評價足見其重要性。

鑒於插件如此重要,內置了眾多的常用的,無需額外安裝就可直接使用。我們先看看的基本配置方法,然後再分類介紹一下常用的。

plugins 的使用方法

是一個數組,數組中的每一項都是某一個的實例,數組甚至可以存在一個插件的多個實例。

下面代碼中,分別展示了內置插件和第三方插件的使用方法:

一種插件其實就是一種函數,通過傳入不同的參數,插件可按我們的需求實現不同的功能。不過插件數量眾多,我們甚至還可以自己來寫插件,每個插件還有自己特定的配置規則,這也是讓人覺得難學的地方之一,不過好在作為一個工具,對於我們大多數人最需要掌握的並不是那麼多,其它的待真的有相關需求再邊查邊學也不遲,的插件列表可參看這裡(https://webpack.js.org/plugins/)。

常用 plugins 的介紹

功能眾多,但是大多數的功能主要集中在兩方面:

對前一階段打包後的代碼進行處理,如添加替換一些內容,分割代碼為多塊,添加一些全局設置等。

輔助輸出,如自動生成帶有鏈接的,對生成文件存儲文件夾做一定的清理等。

對代碼進行處理

:給代碼添加版權信息,如在數組中添加後能在打包生成的所有文件前添加註釋詳見(https://webpack.js.org/plugins/banner-plugin/)。

,用於抽離代碼,具有多種用途 詳情查看CommonsChunkPlugin(https://webpack.js.org/plugins/commons-chunk-plugin)。

抽離不同文件的共享代碼,減少chunk間的重複代碼,有效利用緩存。

抽離可能整個項目都在使用的第三方模塊,比如。

將多個子 chunk 中的共用代碼打包進父 chunk 或使用非同步載入的單獨chunk。

抽離這類每次打包都會變化的內容,減輕打包時候的壓力,提升構建速度。

:使用配置的演算法(如)壓縮打包生成的文件,詳見(https://webpack.js.org/plugins/compression-webpack-plugin)。

:創建一個在編譯時可配置的全局常量,如果你自定義了一個全局變數,可在此設置其值來區分開發還是生產環境詳見(https://webpack.js.org/plugins/define-plugin/)。

:實際上是插件中對進行設置的簡寫形式,如將設置,EnvironmentPlugin(https://webpack.js.org/plugins/environment-plugin/)。

:抽離文件為單獨的文件,詳見(https://webpack.js.org/plugins/extract-text-webpack-plugin)。

:全局自動載入模塊,如添加後,則全局不用在導入就可以直接使用,ProvidePlugin(https://webpack.js.org/plugins/provide-plugin/)。

:使用前需要先安裝,基於壓縮代碼,支持其所有配置UglifyjsWebpackPlugin(https://webpack.js.org/plugins/uglifyjs-webpack-plugin/)。

輔助輸出打包後的代碼

:使用前需要先安裝,為你自動生成一個文件,該文件將自動依據的配置引入依賴,如果你的文件名中添加了等佔位符,這將非常有用, 詳見(https://webpack.js.org/plugins/html-webpack-plugin/)。

:使用前需要先安裝,此插件允許你在配置以後,每次打包時,清空所配置的文件夾,如果你每次打包的文件名不同,這將非常有用 GitHub - clean-webpack-plugin(https://github.com/johnagan/clean-webpack-plugin)。

通過上述對不同插件的描述,你一定大致明白了,插件可以做什麼,之後在開發的過程中,如果你遇到的什麼需要在此階段解決的問題,大可搜索看看是否有相關的插件,推薦查閱awesome-webpack(https://github.com/webpack-contrib/awesome-webpack#webpack-plugins)。

學習了插件以後,現在我們的配置對象是如下這樣:

至此,從輸入->處理->輸出,我們講解了的核心功能,不過還提供其它的一些配置項,這些配置項大多從兩方面起作用,輔助開發、對構建過程中的一些細節做調整。對這些屬性,下面只做簡單的介紹。

其它的一些配置

輔助開發的相關屬性

:

打包後的代碼和原始的代碼往往存在較大的差異,此選項控制是否生成,以及如何生成 source map,用以幫助你進行調試,詳情可查看 Devtool(https://webpack.js.org/configuration/devtool/)。

通過配置選項,你可以開啟一個本地伺服器,為此本地伺服器提供了非常多的配置選項,查看 dev-server (https://webpack.js.org/configuration/dev-server/),你會發現通過合適的配置,你可以擁有所有本地伺服器可提供的功能。

:

啟用 Watch 模式後,將持續監聽任何已解析文件的更改,重新構建文件,Watch 模式默認關閉,在開發時候如果開啟會很方便。

:

一組用來定製 Watch 模式的選項: 詳見 watch(https://webpack.js.org/configuration/watch/)。

:

本配置讓你設置打包後命令行中該如何展示性能提示,比如是否開啟提示,資源如果超過某個大小時該警告還是報錯,詳見 performance (https://webpack.js.org/configuration/performance/)。

:

本選項讓你配置打包過程中輸出的內容,如沒有輸出,標準輸出,全部輸出,只輸出錯誤等等。

精細配置相關屬性

:設置基礎路徑,默認使用當前目錄。

:

確定模塊如何被解析,已經提供了合理的默認值,不過通過你的自定義配置,可以對模塊解析實現更加精細的控制,如對某些常用模塊可以通過設置別名以更容易引用,也可在此處設置可被忽略的後綴名,詳見 resolve(https://webpack.js.org/configuration/resolve/)。

:

告知需要打包的代碼執行的環境,針對 node 和 web 打包過程會有所不同,詳見Target(https://webpack.js.org/configuration/target/)。

:

讓打包生成的代碼中不添加某依賴項,而讓這些依賴項直接從用戶環境中獲取,在進行庫的開發時非常有用。

:

是一個對象,其中每個屬性都是 Node.js 全局變數或模塊的名稱,每一項的設置值都可以是()中的一種,以確定這些node中的對象在其它環境中是否可用。

此外還具備其它一些用的比較少的配置對象,詳見 Other Options(https://webpack.js.org/configuration/other-options/)。

至此,我們了解了常用的配置項及其意義。為了檢測我們的學習成果,我們一起分析一個中等項目中的配置文件。配置文件來自於,使用新建項目後,執行可看到多個配置文件,這裡我們選擇。

分析 create-react-app 中 webpack 的配置

對可能和你看到的有所不同的說明:

之前,對的一些設置會影響這裡看到的配置文件。

原始的中,部分值由外部函數生成,相關值,在上述代碼中直接改為了確定的結果,如在上述代碼中被替換為:

在開發環境並不生成真實的文件到硬碟,上述代碼中的部分路徑可能有誤,見諒。

推薦在看下面的分析前,花三分鐘看看上述文件,如果都能看得懂,那麼恭喜你,你已經明白的運作方式了,快去自己的項目中實踐吧,如果還有疑惑,也不要緊,我們一起來分析。

首先,我們應該明確執行於環境,目的在於返回需要的配置對象,因此其中可以使用node提供的一些特殊變數和語法,比如,又如引入模塊時採用模式。

此文件的開頭,首先通過語句引入了,和一系列插件,除了在前文中我們見過,其它的我們都未曾見過,其實這些大多是針對已有的插件改進或新開發的插件,所以不熟悉也正常,隨後我們將一個個的弄清楚它們是幹嘛的。

對 module.exports 的分析

devtool

此處的配置值為,代表不帶列映射的,將載入的簡化為每行單獨映射。

entry

此處的是一個數組,代表著四項的代碼都會添加到打包結果之中。

可以被看做具有更好體驗的。

用以在瀏覽器中支持。

在開發環境中使用,強制顯示錯誤頁面。

則是我們的的主入口。

output

在實際使用的過程中,我們並看不見開發環境的打包結果,因此此處的說明僅供參考。

指定,打包後文件存放的位置為。

為,在打包文件後,在其中所包含引用模塊的信息,這在開發環境中有利於調試。

指定了打包的名字和基本的引用路徑。

:指定了非入口文件的名稱。

:指定伺服器讀取時的路徑,此處設置為。

:這裡是一個函數,指定了位於磁碟的位置。

resolve

:指定了模塊的搜索的位置,這裡設置為。

:指明在引用模塊時哪些後綴名可以忽略,這裡忽略的文件名包括。

:創建 import 或 require 的別名,使得部分模塊的引用變得簡單,安裝上文的設置,現在我們可以直接引用和了。

:此處使用了的實例,用以限制自己編寫的模塊只能從目錄中引入。

modules

:這裡設置為,表明文件中如果缺少時會直接報錯而不是警告。

Rule1:對文件前置使用,設置格式為。

Rule2:對中的眾多文件類型不使用,並設置其它文件打包後的名稱按格式設置。

Rule3: 對文件調用處理轉換。

Rule4: 對文件,按順序調用,,進行處理。

plugins

這裡的一些插件,有的可能我們還比較陌生,我們一一介紹。

:和串列使用,允許在中添加變數。

:自動生成帶有入口文件引用的。

:當開啟的時候使用該插件會顯示模塊的相對路徑,建議用於開發環境。

:這裡我們設置了的值為。

:啟用模塊熱替換。

:如果路徑有誤則直接報錯。

:此插件允許你安裝庫後自動重新構建打包文件。

:忽略所匹配的。

node

設置的模塊的的值,如果在其它環境中使用時值為。

performance

:不提示測試環境的打包結果。

上文一直討論的是,各設置項的基本意義,目的在於讓你在有相關需求時,能知道該從哪一項下手查詢。不過看到這裡,如果你之前從未上手操作過可能依舊不知道該如何使用,下面我分析一下,我在自己的項目中是如何使用的。

一些工程實踐建議

官方文檔的guides (https://doc.webpack-china.org/guides/)部分已經就如何實踐提出了較多的建議,建議閱讀以下內容前先行閱讀。

結合 npm 使用

在安裝後有多種調用方法。

在命令行中直接傳入參數使用(這個實際我用的比較少)。

自定義文件,在其中完成配置,然後在命令行中執行來使用,配置文件可以是任何其它名稱(如果是,我們直接使用命令)。

結合使用,在文件中的對象中添加相關命令使用,之後通過使用,如下:

上面我們分別構建了和來分別生成開發環境和生產環境的代碼,在命令行中執行和即可生成對應代碼。

為生產環境指定合理的緩存

關於緩存,官方文檔中有一節講解的非常詳細,請參見緩存(https://doc.webpack-china.org/guides/caching)。

合理分割代碼

提供了三種分割代碼的方法,分別是通過,通過插件和通過動態(在中時也常常使用來依據路由分割代碼)。

的配置常用於多頁應用,的使用前文已做簡要敘述,下面簡單敘述下代碼分割原則及我實際工作中是如何使用動態來分割代碼的。

分割原則

目前工作中主要依據兩個原則來分隔代碼:

前端路由:依據路由對應的頁面進行分割,這種分割之後的體驗類似於小程序中每次打開新頁載入對應頁面的js文件。

針對邏輯交互比較複雜的頁面,如果某個較複雜的組件需被某操作觸發後才呈現,也會把該組件分割出來。

分割方法

我們知道動態返回值其實是一個,基於此,對應於我用的React,我常採用以下函數輔助載入。

對代碼的分割方法如下:

簡要的說明一下上述代碼的意義,懶載入函數接受動態的組件和一個載入動畫作為參數,動態的組件載入成功前顯示載入動畫組件,成功後顯示的組件,通過自定義各種各樣的載入動畫,我們可以實現優雅的文件載入過程。

觀察打包後文件的結構,合理進行優化

使用命令可以生成一個包含依賴關係的文件。提供了多種可視化工具幫我們分析這個文件,我最喜歡的工具插件是,可通過下述方法引入該插件:

添加此插件,再次構建完成時,瀏覽器中將自動打開一個類似下面這樣的網頁:

這樣我們可以輕易分析我們的代碼分割是否合理,比如:

分割後文件過大的主要原因是在於引入了那些模塊。

分析大多後的多文件中存不存在對某些比較大的模塊的重複引用,方便我們進一步修正自己的配置文件。

上圖是我之前項目中的一張截圖,第一次見到這張圖時還是給了我很多後期優化的思路的,引用的同時引入了,而實際上該頁面只有一張圖表,這讓我考慮另尋圖表解決方案,,在最初的項目中使用過,後逐步去除,屬於遺留代碼,現在還存在說明在局部可能還是用到了,這都是之後編碼的改進方向。

後記

總覺得技術類的文章也是該有生命力的,花了好久寫完本文,回頭看發現有的內容還是沒有表達或交待清楚。所以有任何建議,請隨意提出,我們在Chat中繼續討論,我也將對本文做長期持續的修改。

針對官網文檔,使用製作了一個思維導圖的草稿,此思維導圖還需完善,之後將持續修改,在此處(https://github.com/zhangwang1990/blogs/tree/master/sources/mindMaps)可查看,該思維導圖示例如下。

另外,關於和的區別,官方文檔中有一部分做了詳細的講解(https://webpack.js.org/guides/migrating/),所以本文中不做贅述,看完以後如果還有疑問,之後我們再詳細討論。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 謝工的GitChat 的精彩文章:

背後那點事兒、職場 PPT 設計
Web 安全 PHP 代碼審查之常規漏洞

TAG:謝工的GitChat |