Skip to content
一个用来移除 data-test 属性的 webpack 插件
JavaScript HTML
Branch: master
Clone or download
Latest commit 1e241f0 Nov 8, 2019
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
plugins first commit Nov 8, 2019
release first commit Nov 8, 2019
src first commit Nov 8, 2019
.babelrc first commit Nov 8, 2019
.gitignore first commit Nov 8, 2019
README.md first commit Nov 8, 2019
index.html first commit Nov 8, 2019
package-lock.json first commit Nov 8, 2019
package.json first commit Nov 8, 2019
webpack.dev.config.js first commit Nov 8, 2019

README.md

自己写一个 Webpack 插件

需求分析

当我们使用 Jest 进行自动化测试时,我们可能会需要在测试文件中获取某个 DOM 节点,这时就会在相应的 DOM 上加一个 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 实际上只是用于测试文件,并没有其它实质性的作用,因此希望在打包时把该属性去掉,这时就可以通过 webpack 插件来清除它,如:

<!-- 未使用移除 data-test 属性插件编译前 -->
<button class="button-class" data-test="button"><button>
<!-- 使用插件后 -->
<button class="button-class"><button>

编写插件

参考官方文档 Writing a Plugin 可知,一个插件由以下几个部分组成:

  • 一个具名的 JavaScript 函数或一个 Class
  • 在它的原型上定义 apply 方法
  • 通过 apply 函数中传入 compiler 并插入指定的事件钩子,在钩子回调中取到 compilation 对象
  • 通过 compilation 处理 webpack 内部特定的实例数据
  • 如果是插件是异步的,在插件的逻辑编写完后调用 webpack 提供的 callback

编写插件

// step1: 创建 plugins/RemoveDataTest.js
class RemoveDataTestPlugin {
  constructor(options) {
    this.options = options
  }

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

    // step3: 插入事件钩子,在回调中取到 compilation
    compiler.hooks.emit.tap('RemoveDataTest', (compilation) => {
      Object.keys(compilation.assets).forEach(filename => {
        // step4: 得到资源内容
        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 钩子可以把编译好的代码发射到指定的 stream 中触发,在执行这个钩子的时候,我们能从回调函数返回的 compilation 对象上拿到已经编译好的 stream

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

访问 compilation 对象

在每一次编译时,都会拿到最新的 compilation 对象,所有的资源都会以 key-value 的形式存在 compilation 对象下的 compilation.assets

遍历 assets

通过遍历 assets,我们可以拿到 main.js 和 index.html 文件,通过 compilation.assets[filename].source() 取得资源内容,最后用正则 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()
  ],
  // ...
}

源码地址

You can’t perform that action at this time.