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

相关推荐