实现一个webpack Loader

Posted on 2018-07-06

Out of the box, webpack only understands JavaScript files. Loaders allow webpack to process other types of files and convert them into valid modules that can be consumed by your application and added to the dependency graph

这里引用官网上的原文,简单来说,webpack中的loader就是用来处理各种类型的文件的,比如后缀为.scss.vue文件等,同时将它们转换为可由应用程序使用并添加到依赖关系图的有效模块。

明确loader的本质

实际上,loader就是一个js模块,该模块接收StringBuffer的数据,并返回处理后的StringBuffer。例如sass-loaderwebpack会将sass文件整体作为输入传递给loader,处理后返回。

loader模板示例

module.exports = function (source) {
  return source
}

实现简单版的sass-loader

简易版sass-loader实现

// sassLoader.js

// node-sass用于编译sass
const sass = require('node-sass')
module.exports = function (source) {
  // 编译sass, 此处返回buffer
  return sass.renderSync({
    data: source
  }).css
}

当我们在webpack中配置loader时,可能还需要传入options时,用于给loader提供自定义功能。此时需要引入loader-utils模块,用来在loader中获取传入的options

例如需要在编译sass文件时,需要压缩编译后的内容,可以通过配置compressed来确定是否需要压缩css

// sassLoader.js

const loaderUtils = require('loader-utils')
const sass = require('node-sass')
module.exports = function(source) {
  const options = loaderUtils.getOptions(this)
  return sass.renderSync({
    data: source,
    outputStyle: options.compressed ? 'compressed' : 'nested'
  }).css
}

对应的webpack配置如下:

// webpack.config.js

const path = require("path")
// 自定义的loader
const sassLoader = require.resolve("./sassLoader")
// 由于webpack编译成js文件,使得css也包含其中,extract-text-webpack-plugin用于从js文件中提取css文件
const ExtractTextPlugin = require('extract-text-webpack-plugin')

module.exports = {
  entry: './custom.scss',
  output: {
    path: path.resolve(__dirname, "./output"),
    filename: "custom.js"
  },
  module: {
    rules: [{
      test: /\.scss$/,
      use: ExtractTextPlugin.extract({
        use: [
          {
            // 处理编译后的css文件
            loader: 'css-loader'
          }, {
            // 自定义loader
            loader: sassLoader,
            options: {
              // 压缩配置
              compressed: true
            }
          }
        ]
      })
    }]
  },
  plugins: [
    new ExtractTextPlugin({
      filename: 'css/[name].[hash].css',
      allChunks: true
    })
  ]
}

loader进阶

缓存

默认情况下,webpack会缓存loader的处理结果,用来提高处理速度。但是你也可以关闭缓存功能。

module.exports = function (source) {
  this.cacheable(false)
  return source
}
异步

有时候,loader的处理是异步的,此时我们不能立刻拿到处理后的内容。这时可以通过回调的形式处理

module.exports = function(content, map, meta) {
  var callback = this.async()
  someAsyncOperation(content, function(err, result, sourceMaps, meta) {
    if (err) return callback(err)
    callback(null, result, sourceMaps, meta)
  })
}
二进制

默认情况下,需要处理的文件都是转换为UTF-8格式的字符串传递给loader的,但有时候需要处理的并不是字符串,而是二进制数据。

module.exports = function(content) {
  assert(content instanceof Buffer)
  return someSyncOperation(content)
};
module.exports.raw = true

总结

本篇主要介绍了webpack loader,以及如何手写一个webpack loader,然而webpack loader的功能却不止这些,具体请查看官网介绍