preface

Children who have used Webpack should know the concept of Loader, so I wonder if you are interested in learning about it with me?

Emmm, what is that?

Yes, you read that right, he’s coming, he’s coming, he’s coming towards us, and he’s giving us a soul question to our face.

What is Loader? What’s the use? What are the characteristics? What loaders are available? How does it work? How to write your own Loader?

So today, let’s learn about it together.

Release notes

When this article is written, it is for Webpack 4.x, (5.x changes little), please note.

What is Loader?

Loader, also known as a Loader, is similar to tasks in other build tools and provides a powerful way to handle front-end build steps.

What is the use of Loader?

Loader enables Webpack to handle non-javascript files (WebPack itself only understands JavaScript). Loader can convert all types of files into valid modules that WebPack can handle, and then you can take advantage of WebPack’s packaging capabilities to process them.

In essence, WebPack Loader converts all types of files into a dependency graph for an application and then into modules that can be referenced directly.

Loader features

  • Loader supports chain transfer and can pipeline resources.
    • A set of chained Loaders will execute in reverse order, with the first loader in the loader chain returning values to the next loader, and the last loader returning JavaScript as expected by WebPack.
    • When chaining multiple Loaders, keep in mind that they will be executed in reverse order. Execute from right to left or bottom up, depending on the array format.
  • The Loader can be synchronous or asynchronous.
  • Loader runs in Node.js and can perform any operation possible.
  • Loader receives query parameters. Used to pass configuration to loader.
  • The Loader can also be configured using the Options object.
  • In addition to using the common package.json main property, you can export a normal NPM module as a Loader by defining a Loader field in package.json.
  • Plug-in (plugins)More features can be added to the Loader.
    • Note: Plugin is an extender that enriches the capabilities of WebPack itself. Plugin plugin plugin plugin plugin plugin plugin plugin plugin plugin plugin plugin plugin plugin plugin plugin .
  • Loader can generate additional arbitrary files.

Loader configuration

configuration

The loader configuration in webpack.config.js is as follows

// webpack.config.js
module.exports = {
  module: {
    rules: [{test: /\.css$/,
        use: [
          {
            loader: 'style-loader'}, {loader: 'css-loader',},],},],},};Copy the code

Configure the Loader in line

import '! style-loader! css-loader! less-loader? name=Rain120! ./styles.less';
Copy the code

The inline import module above corresponds to the following configuration (internally performed transformed rule configuration):

module.exports = {
  // ...
  module: {
    // ...
    rules: [{test: /\.less$/,
        use: [
          {
            loader: 'style-loader'.options: {},}, {loader: 'css-loader'.options: {},}, {loader: 'less-loader'},],},],},// ...
};
Copy the code

Such as:

import '-! my-loader! my-loader2! ./styles.css';
Copy the code

The above statement is converted to the right configuration when executed. Note that the default configuration is not changed, but is converted to the right configuration when executed.

Pre-set all rules and use them! , can be overwritten to any loader in the configuration. For more parameters, see 👇🏻 Loader matching rules.

Loader can pass query parameters through options, for example

{
    // ...
    loader: 'less-loader'.// options: '? name=Rain120&age=18',
    options: {name:'Rain120'.age: 18}}Copy the code

Cli configuration Loader

You can also use the Loader on the CLI

webpack --module-bind jade-loader --module-bind 'css=style-loader! css-loader'Copy the code

This will use Jade-loader for.jade files and style-loader and CSS-loader for.css files.

See WebPack using Loader for more information

Loader type

You can configure the loader type by using Enforce as follows

module.exports = {
  // ...
	module: {
    // ...
    // From bottom to top, css-loader -> style-loader
    rules: [{test: /\.css$/,
        use: {
          loader: 'style-loader'
        },
        enforce:'pre'
      },
      {
        test: /\.css$/,
        use: {
          loader: 'css-loader'}}},// ...
}
Copy the code

In normal loader mode, csS-loader is executed after style-loader. That is, the previous CSS-loader -> style-loader becomes style-loader -> CSS-loader.

Rule. Enforce: pre, post

  • Pre Loader: indicates the pre-loader
    • Configure: Enforce: ‘pre’
  • Normal Loader: normal Loader
    • Configuration: Default
  • Inline Loader: inline Loader
    • Specify that the loader used in the module is inline, for example, import ‘! style-loader! css-loader! less-loader? name=Rain120! ./styles.less’;
  • Post Loader: post-loader
    • Configure: Enforce: ‘post’

Loader matching rule

Of course, Webpack can determine whether to use inline mode or eliminate some pre, post and normal loaders by introducing module path rules.

Note:

This inline pattern, which is not the canonical path format in ES Module, should be avoided because

  1. The details of webPack are coupled in the code
  2. May interfere with IDE path resolution

Here are the rules:

-! : Deletes pre and Normal Loaders that meet the requirements

! : Deletes normal Loaders that match the configuration conditions

!!!!! : Deletes pre, Normal, and Post loaders that meet the requirements

// Disable normal loaders
import { a } from '! ./file1.js';

// Disable preloaders and normal loaders
import { b } from '-! ./file2.js';

// Disable all loaders
import { c } from '!!!!! ./file3.js';
Copy the code

Webpack code logic parsing rules are as follows (5.0.0.beta.15 vs 4.43.0)

// ...
const firstChar = requestWithoutMatchResource.charCodeAt(0);
const secondChar = requestWithoutMatchResource.charCodeAt(1);
// Note 👇🏻👇🏻 port 👇 : The old version uses Char Code to determine whether a special flag is specified
const noPreAutoLoaders = firstChar === 45 && secondChar === 33; // startsWith "-!"
const noAutoLoaders = noPreAutoLoaders || firstChar === 33; // startsWith "!"
const noPrePostAutoLoaders = firstChar === 33 && secondChar === 33; // startsWith "!!" ;
const rawElements = requestWithoutMatchResource
  .slice(noPreAutoLoaders || noPrePostAutoLoaders ? 2 : noAutoLoaders ? 1 : 0)
  .split(/! +/);
// ...
Copy the code

See 5.0.0 beta.15 webpack NormalModuleFactory.js for details

// ...
// Note 👇🏻👇🏻 🏻 🏻: the new version determines whether the beginning of the new version is a special flag
const noPreAutoLoaders = requestWithoutMatchResource.startsWith('-! ');
const noAutoLoaders =
  noPreAutoLoaders || requestWithoutMatchResource.startsWith('! ');
const noPrePostAutoLoaders = requestWithoutMatchResource.startsWith('!!!!! ');
let elements = requestWithoutMatchResource
  .replace(/ ^ -? ! +/.' ')
  .replace(/!!!!! +/g.'! ')
  .split('! ');
let resource = elements.pop();
elements = elements.map(identToLoaderRequest);
// ...
Copy the code

See 4.43.0 Webpack NormalModuleFactory.js for details

Loader to perform

Front knowledge

What is the pitch

Webpack allows a function called pitch to be mounted on a Loader function, which is executed earlier than the Loader itself at runtime. It can block the loader chain.

function pitch(
  // Resource request string after the current loader
  / / to! Split the composition of the string
  remainingRequest: string, 
  // List of loaders experienced before executing the current loader
  // The loader has iterated (pitch) with! Split the composition of the string
  previousRequest: string,
  // Same as the Loader function data, used to pass information that needs to be propagated in the Loader
  // Parameters that can be passed when loaderA is executed or loadera.pitch
  data = {}
) :void {
  // balabala ...
}
Copy the code

An 🌰

module.exports = {
  module: {
    rules: [{test: /\.less$/i,
        use: [
          "style-loader"."css-loader"."less-loader"],},],},};Copy the code

When csS-loader. pitch is executed,

// Loader list and resource path after CSS-loaderremainingRequest = less-loader! ./xxx.less// css-loader Specifies the previous loader list
previousRequest = style-loader
/ / the default value
data = {}
Copy the code

Loader chain execution

The Loader execution sequence follows Last In First Out.

module.exports = {
  // ...
  module: {
    // ...
    rules: [{test: /\.css$/.Cs-loader -> style-loader
        use: ['style-loader'.'css-loader'],},],},};Copy the code

Or you are configured 👇🏻

module.exports = {
  // ...
  module: {
    // ...
    Cs-loader -> style-loader
    rules: [{test: /\.css$/,
        use: {
          loader: 'style-loader'}}, {test: /\.css$/,
        use: {
          loader: 'css-loader'}}},// ...
}
Copy the code

The default normal execution order for each loader is pre -> normal -> inline -> POST. In some cases, loaders only care about the metadata following the request and ignore the results of the previous loader. Before the loader is actually executed (right to left), the pitch method on loader is called from left to right. The pitch phase is executed in the order post > inline > normal > pre. For the following use configuration:

module.exports = {
  / /...
  module: {
    rules: [{/ /...
        use: ['a-loader'.'b-loader'.'c-loader'],},],},// ...
};
Copy the code

The results of pitch and Normal execution are as follows

|- a-loader pitch
  |- b-loader pitch
    |- c-loader pitch
      |- requested module is picked up as a dependency
    |- c-loader normal execution
  |- b-loader normal execution
|- a-loader normal execution
Copy the code

Normal execution

If any pitch returns a value during this process, the loader execution chain is blocked. Webpack skips all subsequent pitches and loaders and goes straight to normal execution of the previous loader.

Refer to the pitching-loader and Rule. Enforce for more information

The realization of the Loader

Note: This is not how to implement a Loader, we will only discuss the implementation principles and official writing principles, as long as you follow, you can definitely implement a Nice Loader, trust yourself.

We know that loader is a Node module exported as a function that is called when loader converts resources.

Realize the principle of

The given function calls the Loader API and is accessed through the This context.

// somepath/loader.js
export default function loader(source) {
  const options = this.getOptions();

  console.log('This loader options'.JSON.stringify(options);

  return `export default The ${JSON.stringify(source)}`;
}
Copy the code

Then modify the configuration file

// webpack.config.js
// ...
module: {
    rules: [{test: /\.txt$/,
        use: {
            loader: path.resolve(__dirname, '.. /somepath/loader.js'),
            options: {
                name: 'Rain120'},},},],},// ...
Copy the code

That’s all.

See Webpack Writing a Loader for more information.

Writing principles

Writing a Loader looks easy, but hopefully you can avoid some problems by following the following rules when implementing it.

  • Single rule: Each Loader does only one thing;
  • Chain calls: Webpack calls each Loader in a chain order;
  • Unified principle: Follow Webpack design rules and structure, input and output are strings, each Loader is completely independent, plug and play;

See the Webpack usage guidelines for more

The resources

Those who have knowledge about Loader

Official Webpack Loader document

Loaders Api

Do you really master loader? 10 q – loader

Webpack series four loader details 1

List of previous articles

Two-dimensional code series — Principles of two-dimensional code (II)

Two-dimensional code series — Common “code “(1)

Link in Linux and several common soft Link applications in Node

Alpha blending mode synthesis algorithm