自己寫一個(gè)Webpack插件

需求分析

當(dāng)我們使用 Jest 進(jìn)行自動(dòng)化測試時(shí),我們可能會(huì)需要在測試文件中獲取某個(gè) DOM 節(jié)點(diǎn),這時(shí)就會(huì)在相應(yīng)的 DOM 上加一個(gè) data-test 屬性來獲取,比如:

<button class="button-class" data-test="button"><button>

在測試文件中判斷 button 是否存在

it('頁面上存在 button', () => {
  const button = document.querySelector('[data-test="button"]')
  expect(button).toBeDefined()
})

因此 data-test 實(shí)際上只是用于測試文件,并沒有其它實(shí)質(zhì)性的作用,因此希望在打包時(shí)把該屬性去掉,這時(shí)就可以通過 webpack 插件來清除它,如:

<!-- 未使用移除 data-test 屬性插件編譯前 -->
<button class="button-class" data-test="button"><button>
<!-- 使用插件后 -->
<button class="button-class"><button>

編寫插件

參考官方文檔 Writing a Plugin 可知,一個(gè)插件由以下幾個(gè)部分組成:

  • 一個(gè)具名的 JavaScript 函數(shù)或一個(gè) Class
  • 在它的原型上定義 apply 方法
  • 通過 apply 函數(shù)中傳入 compiler 并插入指定的事件鉤子,在鉤子回調(diào)中取到 compilation 對象
  • 通過 compilation 處理 webpack 內(nèi)部特定的實(shí)例數(shù)據(jù)
  • 如果是插件是異步的,在插件的邏輯編寫完后調(diào)用 webpack 提供的 callback

編寫插件

// step1: 創(chuàng)建 plugins/RemoveDataTest.js
class RemoveDataTestPlugin {
  constructor(options) {
    this.options = options
  }

  // step2: 需要定義 apply 方法,并傳入 compiler
  apply(compiler) {
    // 匹配所有 data-test 屬性的正則
    const reg = /\s*data-test="(.*?)"/g

    // step3: 插入事件鉤子,在回調(diào)中取到 compilation
    compiler.hooks.emit.tap('RemoveDataTest', (compilation) => {
      Object.keys(compilation.assets).forEach(filename => {
        // step4: 得到資源內(nèi)容
        let content = compilation.assets[filename].source()
        // step5: 清除 html 文件中的 data-test 屬性
        if (/\.html$/.test(filename)) {
          content = content.replace(reg, '')
        }
        // step6: 更新 compilation.assets[filename] 對象
        compilation.assets[filename] = {
          source() {
            return content
          },
          size() {
            return content.length
          }
        }
      })
    })
  }
}

module.exports = RemoveDataTestPlugin

過程分析

compiler 中的 emit 鉤子

emit 鉤子可以把編譯好的代碼發(fā)射到指定的 stream 中觸發(fā),在執(zhí)行這個(gè)鉤子的時(shí)候,我們能從回調(diào)函數(shù)返回的 compilation 對象上拿到已經(jīng)編譯好的 stream

compiler.hooks.emit.tap('自定義事件名', (compilation) => {
  // ...
})

訪問 compilation 對象

在每一次編譯時(shí),都會(huì)拿到最新的 compilation 對象,所有的資源都會(huì)以 key-value 的形式存在 compilation 對象下的 compilation.assets

遍歷 assets

通過遍歷 assets,我們可以拿到 main.js 和 index.html 文件,通過 compilation.assets[filename].source() 取得資源內(nèi)容,最后用正則 replace 方法去掉 data-test

更新 assets

compilation.assets[filename] = {
  source() {
    return content
  },
  size() {
    return content.length
  }
}

使用插件

// webpack.dev.config.js
const RemoveDataTestPlugin = require('./plugins/RemoveDataTestPlugin')

module.exports = {
  // ...
  plugins: [
    // ...
    new RemoveDataTestPlugin()
  ],
  // ...
}

源碼地址

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容