博客 / 詳情

返回

前端工程化7:自定義實現Webpack中的Loader和Plugin

loader對比plugin

  • loader:用於資源加載並處理各種語言的轉換/編譯(例如將不同語言轉換為javascript);
  • plugin:用於資源加載以外的其他打包/壓縮/文件處理等功能;

loader原理,loader的自定義實現

loader 用途

loader 用於資源加載並處理各種語言的轉換/編譯(例如將不同語言轉換為javascript);

loader 實現原理,自定義實現

  1. loader 函數:loader 必須導出一個函數;它對資源文件進行處理後輸出內容;它類似於管道,可以串聯多個 loader,將返回值交給下一個 loader 繼續處理;
  2. loader 輸入:function 接收一個參數作為輸入,該參數的內容為文件內容
  3. loader 輸出:return 一個返回值作為輸出,該返回值為模塊導出的字符串;loader 需要將返回值作為一個模塊導出,才能更好的在webpack打包文件中使用,有三種導出方式;
  • (1)按照 ES Modules 方式導出
  • (2)按照 CommonJS 方式導出
  • (3)loader 處理類似於管道,可以串聯多個 loader,可以直接將返回值交給下一個 loader 繼續處理;
// 自定義loader:
// 功能:導入markdown文件,將markdown轉換成html導出


// 插件:marked實現將markdown內容轉換成html的功能
const marked = require('marked')

// loader需要返回一段js代碼:因為在webpack加載時會將這段代碼拼接到打包代碼中~
// 輸入:function的參數作為輸入值:這裏是原有的markdown內容,source
// 輸出:return一個返回值作為輸出:這裏是轉換成html的內容,html(
// 輸出注意:1、需要將返回值作為一個模塊導出,才能更好的在webpack打包文件中使用
// 輸出注意:2、
module.exports = source => {

    const html = marked(source)

    // webpack將loader加載後會將代碼放到打包文件boundle.js中,一個loader對應一個(function(){}) 模塊;所以在loader中需要導出
    // webpack的打包文件boundle.js中,支持CommonJS的方式、ES Modules的方式導出

    // loader的
    // 1、CommonJS的方式:輸出的模塊轉換為字符串的形式return
    // return `module.exports = ${JSON.stringify(html)}`//JSON.stringify轉換html的換行符和空格

    // 2、ES Modules:webpack內部會自動轉換export default導出的代碼
    // return `export default ${JSON.stringify(html)}`


    // 3、直接返回html,再將結果交給html-loader處理(loader是管道的形式處理,可以串聯)
    return html
}
  1. 使用自定義loader
  module: {
    rules: [
        {// 自定義loader,markdown-loader
            test: /.md$/,
            use: [                    // 串聯多個loader,執行順序從下到上
              'html-loader',          // 安裝html-loader:npm install html-loader --save-dev
              './markdown-loader.js'  // 自定義實現的loader
            ]
        },
    ]
  },

plugin原理,plugin的自定義實現

plugin 用途

用於資源加載以外的其他打包/壓縮/文件處理等功能;

plugin 插件機制(Hook 鈎子機制)

  • 需要了解 taple 鈎子:https://gitee.com/ymcdhr/e-de...
  • webpack 鈎子:https://webpack.docschina.org...

plugin 實現原理,自定義實現:

  1. plugin 是根據webpack生命週期的鈎子機制進行開發的,plugin通過在鈎子中掛載函數實現擴展;webpack鈎子參考文檔:https://webpack.docschina.org...
  2. plugin 必須是一個函數,或者包含apply方法的對象
  • apply 方法有一個參數 compiler;
  • 通過 compiler 可以給 webpack 編譯打包過程中添加鈎子;
  • 通過鈎子的回調函數 callback 拿到打包結果對象 compilation(通過compilation.assets 獲取資源文件信息);
  • 然後對打包結果對象 compilation 進行修改;
// 自定義plugin:
// 功能:將js文件中的註釋刪除調

// 自定義插件
// 自定義插件必須是一個函數,或者包含apply方法的對象
// plugin根據webpack生命週期鈎子函數開發的:https://webpack.docschina.org/api/compiler-hooks/#hooks
// compiler.hooks可以訪問鈎子
// 功能:清除打包過程中的註釋
class MyPlugin {
    apply (compiler){
      console.log('MyPlugin 啓動')
  
      // emit鈎子的執行時機是:輸出打包好的資源文件 asset 到 output 目錄之前執行。
      compiler.hooks.emit.tap('MyPlugin', compilation => {
        // compilation => 可以理解為此次打包的結果
        // compilation.assets => 獲取dist目錄的所有生成的資源文件信息,例如:bundle.js
        for (const name in compilation.assets) {
          // console.log(name)
  
          // 獲取到資源的內容,例如:bundle.js裏面的內容
          console.log(compilation.assets[name].source())
  
          // 判斷文件是否為js文件
          if (name.endsWith('.js')) {
            // 替換掉js文件中的註釋
            const contents = compilation.assets[name].source()
            const withoutComments = contents.replace(/\/\*\*+\*\//g, '')
  
            // 覆蓋原有內容
            compilation.assets[name] = {
              source: () => withoutComments, // 新的內容
              size: () => withoutComments.length // 內容的大小,webpack必須的方法
            }
          }
        }
      })
    }
}


module.exports = MyPlugin
  1. 在 webpack.config.js 配置中引入插件
const MyPlugin = require('./comment-plugin.js')

module.exports = {
  // ...
  plugins: [
    new MyPlugin() // 自定義插件
  ]
}

特別鳴謝:拉勾教育前端高薪訓練營

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.