從上文提煉出 webpack 的一些主要流程
init => run(前兩個一般不用) => compile(編譯開始的時候) => compilation(準備編譯的整個過程) => make(開始編譯) => afterCompile(編譯結束) => seal(對代碼進行封裝,比如優化、合併...) => codeGenerate(生成代碼) => emit(發射,把文件寫到硬盤裏面) => done
插件,就是找個地方插進去
比如:imagemin-webpack-plugin 、clean-webpack-plugin
imagemin-webpack-plugin
- 這是一個壓縮圖片的插件
- 文件目錄非常簡單
- 主要看 apply 這個函數
- 根據看源碼的步驟,跳過聲明
- 直接看最後的 if else,因爲我們一般使用的是 webpack 4 以上的版本,所以進入 compiler.hooks 分支(見代碼備註)
- 發現他監聽了 emit 這個事件
- 直接看函數名字我們就能知道他的主要功能(優化項目中圖片和其他外部圖片)
- 對 compilation.assets (此次編譯的文件)進行 map 操作,用 testFunction 判斷是否是圖片文件,然後執行 optimizeImage 這個方法進行圖片優化(這裏用到了 imagemin 這個庫)
這個插件非常簡單
clean-webpack-plugin
- 這是一個清理 webpack 的輸出目錄的插件
- 分析這個插件之前,我們先思考如果我們要寫這個插件,我們應該監聽哪個鉤子函數?如果是init 可能可以?但是有點不妥,因爲有可能初始化的過程中會報錯,這樣就不會產生新的文件,所以就沒有必要清除之前的文件
- 很顯然我們應該在 emit (寫文件之前)去清理文件
- 果然他也是在監聽 emit,從而去執行 handleInitial 和 removeFiles(看函數名字就知道了,這倆函數邏輯非常簡單,這裏暫不分析了)
- 但是細心的你肯定也發現了,爲什麼下面他還監聽了 done?
- 因爲可能 emit 的時候也會產生一些垃圾、臨時文件,所以也需要清理
ProvidePlugin
- 這個插件主要用於注入全局變量,比如說每個文件都需要用到 JQuery,你需要每個文件都引入,
用這個插件他會幫你自動引入,你直接用就好了 - 使用方法很簡單
- 在選項中聲明好,只要你用到了 $ 或者 JQuery,就會自動引入 JQuery
vue 同理
同樣,如果是我們寫這個插件,我們應該監聽哪個鉤子函數?
猜測應該是在 compilation 或者 make 階段
- 這個插件就不一樣了,他主要監聽的是 nft 的 parse 階段
- 我們來看這個 handle 函數做了什麼
- 遍歷了 definition (就是選項哪個對象: {$: 'jquery', vue: [....]})
- 分析所有的表達式(expression),如果你用到了哪些,就幫你直接在依賴中添加(等價於幫你自動 import)
- 主要思路
正式因爲插件可以在任何階段進行魔改,所以 webpack 功能非常強大
常用插件
loader 與 plugin 的區別
- loader 是在 make 階段執行的,而 plugin 可以在任何一個階段插入,兩者之間沒有關係
- webpack 中 loader 和 plugin 的區別是什麼
- 截取一個最高讚的分析一下
loader 沒啥問題,就是轉換文件
它豐富了webpack本身(難道 loader 沒有豐富 webpack 嗎?)
針對是loader結束後(plugin 可以對任何階段進行操作)
它並不直接操作文件(他可以直接操作文件,甚至可以刪文件)
就這些修正
loader 比較簡單,在 make 階段中執行,單純的文件轉換過程,而 plugin 他是對 webpack 的功能進行拓展,他是基於事件機制,對 webpack 的每個階段都可以進行介入,做一些功能(任何你想做的)。
如何自己寫一個 plugin
必須名聲一個 class
必須有一個 apply 函數,並且接受一個 compiler 參數
接下來對某一個鉤子進行監聽,在寫你的主要邏輯
舉例
一個簡單的在 done 之後打印一句話,和使用方法
用到的主要知識
對 webpack hooks 的瞭解
對編譯原理的瞭解
對 chunk、hash、module、dep、factor 等概念的理解