What is the loader

Webpack can only understand JavaScript and JSON files, which is a built-in capability of WebPack available out of the box. Loader enables WebPack to process other types of files and convert them into valid modules for use by applications and to be added to dependency diagrams. A loader is essentially a JavaScript module exported as a function. — Webpack official Chinese document

As shown in the figure below, during the use of Webpack, the following two forms often appear. The former is to configure loader information according to file matching information in the webpack configuration file. The latter is more about modifying/replacing/generating inline Loader information in the loader/plugin. This involves the classification of loader.

// webpack.config.js
{
  module: {
    rules: [{test: /.txt$/,
        use: [
          {
            loader: getLoader("a-loader.js"),},],enforce: "pre"}, {test: /.txt$/,
        use: [
          {
            loader: getLoader("b-loader.js"),}],enforce: "post",},],},}Copy the code
// app.js
import "/Users/jiangyuereee/Desktop/loader/d-loader.js! ./txt.txt"
Copy the code

The classification of the loader

In Webpack, loaders can be classified into four types: post after, normal, inline, pre before.

enforce

For post, normal, the pre, mainly depends on the Rule in the configuration. The enforce values: the pre | | post, without setting, it is normal.

Note: This is a Rule, not a loader. Apply to all loaders corresponding to the Rule.

inline

The inline loader is special. The loader is written to the code during import/require. For inline, there are three syntax for prefixes:

  • !: ignorenormalloader
  • -! : Ignore preloader and Normalloader

  • !!!!! : Ignore all loaders (pre/norAML/post)

Line loader through! Separate the loaders in the resource, and support? Pass parameters. For details about parameters, see loader.options.

The three prefix syntax is written on the prefix of the inline Loader string to indicate which configuration Loader is ignored

example

For example, a-loader is the Pre loader, B-loader is the Normal loader, and C-loader is the Post loader.

Addendum: All loaders in this article are

module.exports = function (content) {
  console.log("x-loader");
  return content;
};

module.exports.pitch = function (remainingRequest, precedingRequest, data) {
  console.log("x-loader-pitch");
};
Copy the code
  • Prefix-free information
import "/Users/jiangyuereee/Desktop/loader/d-loader.js! ./txt.txt"

c-loader-pitch
d-loader-pitch
b-loader-pitch
a-loader-pitch
a-loader
b-loader
d-loader
c-loader
Copy the code
  • ! The prefix information
import ! "" /Users/jiangyuereee/Desktop/loader/d-loader.js! ./txt.txt";

c-loader-pitch
d-loader-pitch
a-loader-pitch
a-loader
d-loader
c-loader
Copy the code
  • -! The prefix information
import "-! /Users/jiangyuereee/Desktop/loader/d-loader.js! ./txt.txt"; c-loader-pitch d-loader-pitch d-loader c-loaderCopy the code
  • !!!!! The prefix information
import "!!!!! /Users/jiangyuereee/Desktop/loader/d-loader.js! ./txt.txt";

d-loader-pitch
d-loader
Copy the code

Priority of the Loader

The sequence of the four loader calls is: Pre > Normal > Inline > POST.

In the case of the same type of Loader, the priority of invocation is bottom-up and from right to left. (In the case of pitch, vice versa).

Such as:

{
  module: {
    rules: [{test: /.txt$/,
        use: [
          {
            loader: getLoader("a-loader.js"),},],enforce: "post"}, {test: /.txt$/,
        use: [
          {
            loader: getLoader("b-loader.js"),}, {loader: getLoader("c-loader.js"),},],enforce: "post",},],},}Copy the code
a-loader-pitch
b-loader-pitch
c-loader-pitch
c-loader 
b-loader 
a-loader 
Copy the code

Loader invocation chain

Each loader has its own normal function and pitch function, and the calling process is to call the respective Pitch function of loader according to the priority order from low to high, and then use the respective Normal function from high to low. The process is more like an onion model.

Loader – pitch

Loader is always called from right to left. In some cases, the loader only cares about the metadata following the request and ignores the results of the previous loader. The pitch method on loader is called from left to right before the loader is actually executed (right to left). — Webpack official Chinese document

For a loader, it is possible to export the pitch method through module.exports in addition to the module. Exports handler. Normally, a left-to-right pitch method call is made before loader calls from right to left, and during pitch calls, if any of them return a value, the subsequent loader call chain will be blocked and its return result will be passed to the previous loader as content.

Note: The pitch method can also be synchronous or asynchronous. You can also choose return or this.callback to convey more information, which will be discussed later.

Loader call process:

If one of the pitches returns a result, subsequent loaders will be skipped and the result will be passed to the previous loader.

For common style-loader/vue-loader, pitch stage is used to intercept processing work, so as to achieve loader specialization work.

Synchronous/asynchronous Loader

If the result is a single process, it can be returned directly in synchronous mode. If there are multiple processing results, this.callback() must be called. In asynchronous mode, this.async() must be called to tell the Loader runner to wait for the asynchronous result, which returns the this.callback() callback function. The loader must then return undefined and call the callback function. — Webpack official Chinese document

In Webpack, the loader may be time-consuming because it relies on reading external configuration files, making network requests, and so on. If synchronous operation is performed, webpack will be blocked, so loader will be divided into synchronous and asynchronous.

In loader, data can be returned in two ways:

  • return:returnCan only returncontentInformation;
  • callback:
this.callback(
  err: Error | null.// Error message
  content: string | Buffer,    / / the content informationsourceMap? : SourceMap,// sourceMapmeta? : any// Ignored by Webpack, can be anything (e.g. AST, some metadata, etc.).
);
Copy the code

Synchronous loader

For synchronous loaders, use either return or this.callback to achieve the desired effect. This. Callback returns more information than return.

module.exports = function(content, map, meta) {
  // return handleData(content);
  this.callback(null, handleData(content), handleSourceMap(map), meta);
  return; // Always return undefined when callback() is called
};
Copy the code

Asynchronous loader

For asynchronous loaders, this.async() is used to retrieve the callback function.

module.exports = function(content, map, meta) {
  var callback = this.async();
  asycnHandleData(content, function(err, result) {
    if (err) return callback(err);
    callback(null, result, map, meta);
  });
};
Copy the code

The parameters of the loader

Generally, the starter loader has only one input parameter: the content of the resource file. By default, the content of the resource file is passed to the Loader as a UTF-8 string. Loaders can set module.exports.raw = true if necessary. We need to accept a Buffer. At the same time, each loader can pass String | | Buffer, complier between loader will transform according to the value of the raw. (e.g. image files, etc.)

In addition to the start loader, loader can accept three parameters: Content, sourceMap, and Meta.

Loader. The parameters of the pitch

Pitch Consists of three parameters:

  • remainingRequest: All Loaders on the right of the current loader plus resource paths, according to!Split, connected from the inline loader.
  • PrecedingRequest: All loaders on the left of the current loader, according to! Split, connected from the inline loader.

  • Data: Data objects shared between pitch and Normal phases. That is, data in the pitch phase and data obtained through this.data in the Normal phase are the same object.

Addendum: Left and right side, relative to loader call chain, normal phase.

example

For example, if there are three Loaders A, B, and C, (use: [a, B, C]), pitch parameters of a, B, and C are as follows:

  • A:remainingRequest: b – loader. Js! c-loader.js! File. TXT;precedingRequest: “‘;
  • B: remainingRequest: C-loader.js! File. TXT; PrecedingRequest: a – loader. Js;

  • C: remainingRequest: file.txt; precedingRequest: a-loader.js! B – loader. Js;

The output of the loader

Compiler expects the result of the processing produced by the last loader. The result should be of type String or Buffer (which can be converted to String), representing the module’s JavaScript source code. Alternatively, you can pass an optional SourceMap result in the form of a JSON object. — Webpack official Chinese document

When the loader link reaches the last loader, the compiler expects the processing result to be a Buffer or String type that can be converted to a String, representing the JS source code after the current template processing. A sourceMap can also be passed, according to the callback function.

In addition to the output source code returned by normal Loader processing, additional output files can be made from this.emitfile.

emitFile(
    name: string, 
    content: Buffer|string, sourceMap? : {... });Copy the code

The loader cache

Development environment By default, loader processing results are marked as cacheable. Since many loader conversion processes are time-consuming, WebPack marks all Loaders as cacheable by default and does not re-process the loader unless the dependent files have changed.

You can mark a Loader as not cacheable with this.cacheable=false. In most cases, it is not recommended to mark a Loader as not cacheable. You can add file dependencies using this.addDependency.

example

{
  entry: "./app.js".mode: "development".module: {
    rules: [{test: /.txt$/,
        use: [
          {
            loader: getLoader("a-loader.js"),},],},]}}Copy the code

In watch mode, recompilation does not call a-loader to reprocess the.txt file if the.txt file is not changed. If you expect the A-loader to be called again when a.js file changes, you can add file dependencies using this.adddependency.

module.exports = function (content) {
  console.log("a-loader");

  this.addDependency("/Users/jiangyuereee/Desktop/loader/c.js");
  return content;
};
Copy the code

Note: The entire loader call chain is reactivated.

loaderAPI

In addition to some of The apis mentioned above, there are several other apis available within The Loader: The Loader Context

Loader Development Kit

  1. Loader-runner

You can use this package to develop and debug loader.

  1. Loader-utils

It integrates some commonly used methods in loader development to facilitate development.

  1. Schema-utils

This helps verify the validity of Loader options (including but not limited to loader usage).

The resources

Webpack official Chinese document

Loader mechanism

— Welcome to “byte front-end ByteFE” resume delivery email “[email protected]