80行代碼寫一個Webpack插件並發佈到npm
1. 前言
最近在學習 Webpack
相關的原理,以前隻知道 Webpack 的配置方法,但並不知道其內部流程,經過一輪的學習,感覺獲益良多,為瞭鞏固學習的內容,我決定嘗試自己動手寫一個插件。
這個插件實現的功能比較簡單:
- 默認清除
js
代碼中的console.log
的打印輸出; - 可通過傳入配置,實現移除
console
的其它方法,如console.warn
、console.error
等;
2. Webpack 的構建流程以及 plugin 的原理
2.1 Webpack 構建流程
Webpack
的主要構建流程,可以分為三個階段:
- 初始化階段:啟動構建,讀取與合並配置參數,加載
Plugin
,實例化Compiler
。 - 編譯階段:從
Entry
發出,針對每個Module
串行調用對應的Loader
去翻譯文件內容,再找到該Module
依賴的Module
,遞歸地進行編譯處理。 - 生成階段:對編譯後的
Module
組合成Chunk
,把Chunk
轉換成文件,輸出到文件系統。
如果 Webpack
打包生產環境文件時,隻會執行一次構建,以上階段會按順序執行一遍。但是在開啟監聽模式時,如開發環境,Webpack 會持續的進行構建。
2.2 plugin 原理
Webpack
插件通常是一個帶有 apply
函數的類,其中 constructor
可以接收傳入的配置項。插件被安裝時,apply
函數會被調用一次,並接收 Compiler
對象,然後我們可以在 Compiler
對象上監聽不同的事件鉤子,從而進行插件功能的開發。
// 定義一個插件 class MyPlugin { // 構造函數,接收插件的配置項 options constructor(options) { // 獲取配置項,初始化插件 } // 插件安裝時會調用 apply,並傳入 compiler apply(compiler) { // 獲取 comolier 獨享,可以監聽事件鉤子 // 功能開發 ... } }
2.3 compiler 和 compilation 對象
在開發 Plugin
過程中最常用的兩個對象就是 Compiler
和 Compilation
:
Compiler
對象在Webpack
啟動時被實例化,該對象包含瞭Webpack
環境所有的配置信息,包括options
、loaders
、plugins
等。在整個Webpack
構建過程中,Compiler
對象是全局唯一的, 它提供瞭很多事件鉤子回調供插件使用。Compilation
對象包含瞭當前的模塊資源、編譯生成資源、變化的文件等。Compilation
對象在Webpack
構建過程中並不是唯一的,如果在開發模式下Webpack
開啟瞭文件檢測功能,每當文件變化時,Webpack
會重新構建,此時會生成一個新的Compilation
對象。Compilation
對象也提供瞭很多事件回調供插件做擴展。
3. 插件開發
3.1 項目目錄
該插件實現的功能比較簡單,文件目錄也不復雜。首先新建一個空文件夾 remove-console-Webpack-plugin
,並在該文件夾目錄下運行 npm init
,根據提示來填寫 package.json
相關信息。然後再新建一個 src
文件夾,插件主要代碼就放在 src/index.js
裡面。如果你需要把項目放到 github
上,最好也添加一下 .gitignore
、README.md
等文件。
// remove-console-Webpack-plugin ├─src │ └─index.js ├─.gitignore ├─package.json └─README.md
3.2 插件代碼
插件代碼邏輯也並不復雜,主要有幾點:
- 在構造函數中接收配置參數,並對參數進行合並,得到需要清除的
console
函數, 存放在removed
數組中; - 在
apply
函數中監聽compiler.hook.compilation
鉤子,該鉤子觸發後,拿到compilation
後進一步監聽它的鉤子,這裡Webpack4
和Webpack5
的鉤子不一樣,需要做兼容; - 定義
assetsHandler
方法來處理js
文件,利用正則表達式清除removed
中包括的console
函數;
class RemoveConsoleWebpackPlugin { // 構造函數接受配置參數 constructor(options) { let include = options && options.include; let removed = ['log']; // 默認清除的方法 if (include) { if (!Array.isArray(include)) { console.error('options.include must be an Array.'); } else if (include.includes('*')) { // 傳入 * 表示清除所有 console 的方法 removed = Object.keys(console).filter(fn => { return typeof console[fn] === 'function'; }) } else { removed = include; // 根據傳入配置覆蓋 } } this.removed = removed; } // Webpack 會調用插件實例的 apply 方法,並傳入compiler 對象 apply(compiler) { // js 資源代碼處理函數 let assetsHandler = (assets, compilation) => { let removedStr = this.removed.reduce((a, b) => (a + '|' + b)); let reDict = { 1: [RegExp(`\\.console\\.(${removedStr})\\(\\)`, 'g'), ''], 2: [RegExp(`\\.console\\.(${removedStr})\\(`, 'g'), ';('], 3: [RegExp(`console\\.(${removedStr})\\(\\)`, 'g'), ''], 4: [RegExp(`console\\.(${removedStr})\\(`, 'g'), '('] } Object.entries(assets).forEach(([filename, source]) => { // 匹配js文件 if (/\.js$/.test(filename)) { // 處理前文件內容 let outputContent = source.source(); Object.keys(reDict).forEach(i => { let [re, s] = reDict[i]; outputContent = outputContent.replace(re, s); }) compilation.assets[filename] = { // 返回文件內容 source: () => { return outputContent }, // 返回文件大小 size: () => { return Buffer.byteLength(outputContent, 'utf8') } } } }) } /** * 通過 compiler.hooks.compilation.tap 監聽事件 * 在回調方法中獲取到 compilation 對象 */ compiler.hooks.compilation.tap('RemoveConsoleWebpackPlugin', compilation => { // Webpack 5 if (compilation.hooks.processAssets) { compilation.hooks.processAssets.tap( { name: 'RemoveConsoleWebpackPlugin' }, assets => assetsHandler(assets, compilation) ); } else if (compilation.hooks.optimizeAssets) { // Webpack 4 compilation.hooks.optimizeAssets.tap( 'RemoveConsoleWebpackPlugin', assets => assetsHandler(assets, compilation) ); } }) } } // export Plugin module.exports = RemoveConsoleWebpackPlugin;
4. 發佈到npm
希望別人能使用到你的插件,就需要把插件發佈到 npm
上,發佈的主要流程:
首先在 npm
官網上註冊賬號,然後打開命令行工具,在任意目錄下輸入 npm login
並按提示登錄;
登錄後可用 npm whoami
查看是否登錄成功;
發佈前檢查一下根目錄下的 package.json
文件信息是否填寫正確,主要字段:
- name:決定用戶下載你的插件時用的名稱,不可與
npm
上已有的第三方包重名,否則無法發佈; - main:插件主文件入口,
Webpack
引入插件時,就從該目錄導入; - version:每次更新發佈時,需要與上一版本的版本號不一樣,否則上傳不成功;
- repository:如果你的插件代碼放在
github
、gitee
等網站,可以填一下; - private:不能設置為
true
,否則無法發佈;
一切準備就緒後,切換到插件所在的目錄下,運行 npm publish
即可上傳插件;
上傳成功後,到 npm
官網上搜索,看看是否能搜到插件;
5. 結尾
到此這篇關於80行代碼寫一個Webpack插件並發佈到npm的文章就介紹到這瞭,更多相關Webpack插件發佈到npm內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 淺談Webpack4 plugins 實現原理
- vue-loader和webpack項目配置及npm錯誤問題的解決
- webpack搭建vue環境時報錯異常解決
- 教你使用webpack打包編譯TypeScript代碼
- 使用webpack打包ts代碼的實現