需求分析
當(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()
],
// ...
}