What is the Loader

Loader is a very important concept in Webpack. Loader can be simply understood as a file processor. Webpack uses Loader to process all kinds of files, such as.scSS into CSS files and base64 images.

Essentially, a loader is just a JavaScript module exported as a function that is called when Webpack is packaged to pass in the results or resource files generated by the previous Loader.

This time I plan to develop a Loader to convert ordinary images to.webp format to achieve the purpose of reducing the size of images.

Loader tool library

Before developing a Loader, learn about two toolkits for loader development – loader-utils and schema-utils

  • loader-utilsUsed to obtain the loaderoptions.
  • schema-utilsUsed to verify the LoaderoptionsJSON SchemaIs the structure defined consistently?
import { getOptions } from 'loader-utils';
import { validateOptions } from 'schema-utils';
const schema = {
  // ...
}
export default function(source) {
  / / get the options
  const options = getOptions(this);
  // Check whether loader options are valid
  validateOptions(schema, options, 'Demo Loader');

  // Write the conversion loader logic here
  // ...
};
Copy the code

Loader development Criteria

  • Single responsibility: A loader handles only one task, which is simple and maintainable. Complex scenarios can be completed by combining multiple Loaders.
  • Modularity: Ensure that the Loader is modular. The generated modules of loader must comply with the same design principles as common modules.
  • Stateless: We should not keep state in the Loader between module transitions. Each loader runtime should be independent of other compiled modules and should also be independent of previous loaders’ compilations of the same module.

In short, when we write a loader, we should keep its responsibilities simple and only care about the input and output.

Synchronous and asynchronous Loaders

Loaders are classified into synchronous and asynchronous because some resources are time-consuming to be processed asynchronously and can be executed after the processing is complete. Loader returns this.callback() as follows:

this.callback(
  err: Error | null.content: string | Buffer, sourceMap? : SourceMap, meta? : any );Copy the code
  1. The first argument must be Error or NULL
  2. The second argument is a string or Buffer.
  3. Optional: The third parameter must be a source map that can be parsed by the module.
  4. Optional: The fourth option, which is ignored by Webpack, can be anything (such as some metadata).
// Synchronize loader returns multiple processing results
module.exports = function(content, map, meta) {
  this.callback(null, someSyncOperation(content), map, meta);
};
Copy the code

In synchronous mode, if only one result is returned, you can use return to return the result.

// The synchronization loader returns only one processing result
module.exports = function(content, map, meta) {
  return someSyncOperation(content);
};
Copy the code

This. Async is used to retrieve callback functions in asynchronous mode

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);
  });
};
Copy the code

Develop your own Loader

Project directory structure

| – SRC | | – CJS. Js / / commonJs entrance | | – index. The js / / loader master file | | – options. The json / / loader options definition file | — Babel. Config. Js |–package.json |–readme.md

Define the options

The quality attribute is used to control the quality of the generated.webp file.

{
  "additionalProperties": true."properties": {
    "quality": {
      "description": "quality factor (0:small.. 100:big), default=75"."type": "number"}},"type": "object"
}
Copy the code

Image format conversion

Cwebp this JS module provides ordinary pictures and. Webp between the conversion function.

const CWebp = require('cwebp').CWebp

/ * * * ordinary images. Webp image * @ param {string | buffer} img image absolute path or binary stream * @ param {number} quality generated webp image quality, The default is 75 * @returns. Webp file stream */
async function convertToWebp (img, quality = 75) {
  let encoder = new CWebp(img)
  encoder.quality = quality
  let buffer = await encoder.toBuffer()
  return buffer
}
Copy the code

Write the loader

import schema from './options.json'

export default async function loader (content) {
  // Asynchronous mode
  let callback = this.async()
  / / get the options
  const options = loaderUtils.getOptions(this) | | {}/ / verification options
  validateOptions(schema, options, {
    name: 'webp Loader'.baseDataPath: 'options'
  })
  try {
    // Go to.webp
    let buffer = await convertToWebp(content, options.quality)
    callback(null, buffer)
  } catch (err) {
    callback(err)
  }
}
// loader receives file streams
export const raw = true
Copy the code

At this point the Loader is almost complete.

use

// webpack.config.js
module.exports = {
  // Other configurations
  // ...
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/.use: [{
        loader: 'file-loader'.options: {
          name: '[name].[ext].webp'
        }
      },{
        loader: 'webp-loader'.options: {
          quality: 70}}]}}Copy the code

conclusion

In fact, writing a Loader is not as complicated as imagined, as long as you master the basic points, you can also have your own Loader.

Pay attention to our