乐趣区

webpack loader和plugin

适用 webpack3。
loader
loader 主要用于预处理源文件,类似于构建工具中的任务概念
开始一段简单的 loader 编写
编写一段 js 代码,如
function loader (source) {
var self = this;
return source.replace(/<div(.*?)>/, function (str) {
var value = convertStr(self.resource)
return str.substring(0, str.length – 1) + ‘ data-source=”‘ + value.substring(value.indexOf(‘src’)) + ‘”>’
})
}

function convertStr (str) {
return str.replace(/\\/g, function (v) {
return ‘/’
})
}

module.exports = loader;
其中 source 返回的是源文件内容或者上一个 loader 返回的内容。没错,loader 可以有多个,并且按照从后往前的顺序执行。
this 是 webpack 的实例,this.resource 获得当前的文件地址。
在 webpack 配置文件中,配置
resolveLoader: {
modules: [
path.resolve(__dirname, ‘../build/rules’),
‘node_modules’
]
},
因为是本地自制的 loader,需要声明地址。默认会直接寻找 node_modules 中的 loader。
{
test: /\.vue$/,
use: [
{
loader: ‘vue-loader’,
options: vueLoaderConfig
},
{
loader: ‘vue-source-loader’
}
]
},
和 vue-loader 一起监听.vue 文件。vue-source-loader 是第一个执行的 loader
plugin
plugin 主要完成 loader 无法完成的事情
plugin 的主要执行 apply 方法, 在 plugin 中存在很多 hook 钩子,即生命周期
阅读 webpack 所有的 HOOK
一个小栗子(完成后效果和上面的 loader 一致,但是十分复杂且不利于阅读, 仅供了解,因为是修改打包后的文件,定有不足之处)
function MixBase () {
}

MixBase.prototype.apply = function (compiler) {
compiler.plugin(’emit’, function (compilation, callback) {
var arr = new Set()
compilation.chunks.forEach(function (chunk) {
chunk.modules.forEach(function (module) {
module.fileDependencies.forEach(function (filepath) {
if (/\.vue$/.test(filepath)) {
arr.add(filepath)
}
})
})

chunk.files.forEach(function (filename) {
if (/\.js$/.test(filename)) {
var source = compilation.assets[filename].source(),
name = source.match(/name:[\’|\”](.*?)[\’|\”]/g),
isApp = false,
finalSource,
componentsArr = []

if (name) {
for (var i = 0; i < name.length; i++) {
var item = name[i].match(/name:[\’|\”](.*?)[\’|\”]/)[1]
if (item === ‘App’) isApp = true
componentsArr.push(item)
}

if (isApp) {
finalSource = source.replace(/;return [a-z|A-Z]\([\”|\’](.*?);/, function (v) {
return v.replace(/attrs:\{id:(.*?)\}/, function (k) {
return k.substring(0, k.length – 1) + ‘,”data-source”:”src/app.vue”}’
})
})
} else {
finalSource = source.replace(/;return [a-z|A-Z]\([\”|\’](.*?),/, function (v) {
var newStr
for (var k of arr) {
if (k.indexOf(componentsArr[0]) !== -1) {
newStr = k.replace(/\\/g, function (str) {
return ‘/’
})
newStr = newStr.substring(newStr.indexOf(‘src’))
}
}
return v + ‘{attrs:{“data-source”:”‘ + newStr + ‘”}},’
})

for (var j = 1; j < componentsArr.length; j++) {
if (componentsArr[j].trim()) {
var reg = new RegExp(`,[a-z|A-Z]\\([\\”|\\’]${componentsArr[j].toLocaleLowerCase()}(.*?)\\)`)
console.log(`,[a-z|A-Z]\\([\\”|\\’]${componentsArr[j].toLocaleLowerCase()}(.*?)\\)`)
finalSource = finalSource.replace(reg, function (v) {
var newStr
for (var k of arr) {
if (k.indexOf(componentsArr[j]) !== -1) {
newStr = k.replace(/\\/g, function (str) {
return ‘/’
})
newStr = newStr.substring(newStr.indexOf(‘src’))
}
}
return v.substring(0, v.length – 1) + ‘,{attrs:{“data-source”:”‘ + newStr + ‘”}})’
})
}
}
}

compilation.assets[filename] = {
source: function () {
return finalSource
},
size: function () {
return finalSource.length
}
}
}
}
})
})
callback()
})
}

module.exports = MixBase

退出移动版