Before we begin, let’s take a look at loader. The following is a description of Loader from WebPack Chinese

A loader is simply a JavaScript module exported as a function. The Loader Runner calls this function and passes in the result or resource file generated by the previous loader. The function’s this context will be populated by Webpack, and the Loader Runner has some useful methods to change the Loader to make asynchronous calls, or to get query parameters.

Loader parameters and return values (also extracted fromWebpack Chinese website)

The first loader passes in only one argument: the contents of the resource file. Compiler needs to get the processing results generated by the last loader. The result should be a String or Buffer (converted to a String) that represents the JavaScript source code for the module. An optional SourceMap result (formatted as a JSON object) can also be passed.

The classification of the loader

Loaders are classified into synchronous and asynchronous loaders. Loader is essentially a function. Therefore, the difference between the two loaders can be understood as only the return value is different (just for easy analogy). The actual operation is not simply two return values.

Synchronous loader

Synchronizing the Loader is relatively simple. Either a return or a call to this.callback can return the converted content synchronously.

  1. usereturnIt’s kind of intuitive, just a normal function that returns a value when it’s done. Such as:
function loader(source) {
  // do anything
  return source;
}
Copy the code
  1. usethis.callback this.callbackThe function argument type of
// The last two parameters are non-mandatory
this.callback(
  err: Error | null.content: string | Buffer, sourceMap? : SourceMap, meta? : any );Copy the code

This is more flexible than the return method and can return multiple values. Such as:

function loader(source) {
  // do anything
   this.callback(null, source, map, meta);
}
Copy the code

Note: After a callback is called in a function, the return value of the return is ignored.

Asynchronous loader

There is only one processing method for an asynchronous loader. For asynchronous loaders, use this.async to get the callback function. The callback function is the same as the callback function in the sync loader.

function loader (source) {
    console.log('--- loader begin ----');
    console.log(source);
    console.log('--- loader end ----\n');
    const callback = this.async();
    // do anything
    callback(null, source);
}

module.exports = loader;
Copy the code

Note some special cases:

  1. Call from a functionthis.asyncLater,returnIs ignored.
  2. Call from a functionthis.asyncAfter, call againthis.callback, will be the currentloaderAs aSynchronous loaderTo deal with.
  3. Call from a functionthis.asyncAfter, call againthis.callbackAnd immediately callCallback returned by this.async. The program will report an error.

At this point, we are ready to start writing a simple Loader. Let’s start by writing a few loaders

banner-loader

The loader adds a line comment to each file header, identifying the author. Such as:

/**
** @author laibao101
** @time 2019-06-04 19:57:20
**/
// source code
Copy the code

Function analysis: This loader does not have any asynchronous operation, so we can use the synchronous loader. Note, for the moment write dead, through the parameter configuration, and then do.

function loader (source) {
    const time = new Date().toLocaleString()
    return `
        /**
        ** @author laibao101
        ** @time ${time}* * /${source}
    `
}
Copy the code

Using the template string, write the comment code and return it. This completes a simple loader.

Asynchronous banner – loader

This loader is written to write an asynchronous loader. The function reads a template from a file and adds a banner to the file

// banner-loader
const path = require('path');
const fs = require('fs');
const baseUrl = process.cwd();
const configTextPath = "bannerConfig.txt";
// Concatenate the complete configuration file path
const fullConfigPath = path.resolve(baseUrl, configTextPath);
// Get the data according to the placeholder in the template
const configItemMap = {
    author: "laibao101".time: new Date().toLocaleString()
};
// Matches placeholders
const reg = /{(\w+)}/gi;

function loader (source) {
    const callback = this.async();
    fs.readFile(fullConfigPath, (err, data) => {
        if (err) {
            // If reading the file fails, an error is returned
            callback(err);
        }
        // Get the file contents
        const template = data.toString();
        // According to the template, modify the placeholder data to complete the banner
        const banner = template.replace(reg, (match, key) => {
            return configItemMap[key] || key;
        });
        // Concatenate the return value
        const ret = `
            ${banner}
            ${source}
        `
        callback(null, ret);
    });
}

module.exports = loader;


// bannerConfig.txt

/**
** @author {author}
** @time {time}
**/
Copy the code

The synchronization banner-loader parameter can be configured

The banner-loader parameters are set internally by loader. If you need to get parameters from webpack.config.js, you can use the this.query parameter provided by webpack to get the options parameter in the Loader configuration. This is used frequently in Loaders.

For example: Babel – loader

module: {
  rules: [{test: /\.m? js$/.exclude: /(node_modules|bower_components)/.use: {
        loader: 'babel-loader'.options: {
          presets: ['@babel/preset-env'}}}]}Copy the code

url-loader

module.exports = {
  module: {
    rules: [{test: /\.(png|jpg|gif)$/i.use: [{loader: 'url-loader'.options: {
              limit: 8192}}]}}Copy the code

So, we modify the banner-loader above to get the parameter from the Loader configuration

// banner-loader
function loader (source) {
    const time = new Date().toLocaleTimeString();
    const { author = ' ' } = this.query || {}
    return `
        /**
        ** @author ${author}
        ** @time ${time}* * /${source}
    `
}

module.exports = loader;


// webpack.config.js{... options: {author: "laibao101"}}Copy the code

Thus, we can implement the Loader whose parameters are retrieved from the configuration file.

The next article

This section describes raw-loader and PITch-loader. And write a demo