适用webpack3。loaderloader主要用于预处理源文件,类似于构建工具中的任务概念开始一段简单的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是第一个执行的loaderpluginplugin主要完成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