Writing in the front

Most front-end developers probably know that loader has two execution stages, normal and PITCH, but most students’ understanding of pitch Loader is just as follows:

  • pitch loaderWhat is the meaning of.
  • pitch loaderWhat kind of effect will be produced.

Once the overhand loader is developed, it is not clear what role pitch Loader will bring to us in real business scenarios and what the application scenarios of real PITCH Loader are when developing relevant loader.

And when designing a Loader application, we have no idea whether the loader logic should be designed in normal stage or pitch stage.

For how to design a Loader, most developers may only stay in the basic stage, but it is often these details of the knowledge control is the embodiment of a software development engineer’s comprehensive ability.

Here, the purpose of this article is to guide you to peep into the real application scenarios of PITCH Loader from the design philosophy of open source Loader project, and to guide you to really master the applicable scenarios of PITCH Loader in actual development.

If you don’t know webPack Loader, don’t worry. We’ll start with a few simple pictures to explain what pitch Loader and Normal Loader are.

What is a Loader pitch

We’re going to talk a little bit about the basics before we get started.

For loader advanced knowledge, friends who want to know can check this article :[Multi-angle analysis of Webpack5 Loader core principle], covering loader basic application, source code implementation, enterprise loader development of all aspects of the explanation.

The kinds of loader

Loaders can be classified into four types

Loaders in Webpack are divided into four stages: Pre, Normal, Inline, and POST. They are distinguished by their names: the four loaders execute in different order.

  1. Pitching phase: Pitch method on the loader called in post, inline, normal, pre order.

  2. Normal phase: a Normal method on the Loader, called in the order pre, Normal, inline, and post. Conversion of module source code occurs in this phase.

If you don’t know what normal and pitch are at this point, it doesn’t matter that you need to simply remember that there are four types of loaders. They are normally executed in the order pre, normal, inline, and post.

Specify Loader type

In the webpack configuration file, we can specify the type of loader by using the rule-enforce configuration item on the Module object. From the configuration file, we can configure three types of Loader:

  • If Enforce is pre, the Loader in this configuration item is the pre Loader.

  • If Enforce is set to POST, the loader in this configuration item is post loader.

  • When enforce is not configured, the loader in this configuration item is the default Normal loader.

Enforce: Inline inline: use inline to configure enforce:inline.

The inline inline-loader is not configured in the Webpack configuration file; it is configured in the module import statement of our business code.

import Styles from 'style-loader! css-loader? modules! ./styles.css';
Copy the code

For example, here we pass the import Styles from ‘./ Styles’ module with! Two inline loaders are configured for the split rule, style-loader and CSS-loader respectively.

The execution sequence of the inline loader is also from right to left. That is, the CSS-loader processing file is executed first after the style-loader processing.

When using the inline loader, you can configure additional rules

You can override all loaders, preloaders, and postloaders in [configuration] by prefixing inline import statements:

  • Use! Prefix to disable all configured Normal Loaders

    import Styles from '! style-loader! css-loader? modules! ./styles.css';
    Copy the code
  • Use!!!!! Prefix to disable all configured loaders (preLoader, loader, postLoader)

    import Styles from '!!!!! style-loader! css-loader? modules! ./styles.css';
    Copy the code
  • Use -! Prefix, disables all preloaders and loaders configured, but disables postLoaders

    import Styles from '-! style-loader! css-loader? modules! ./styles.css';
    Copy the code

Here is a relatively simple, is to set the prefix to disable different types of Loader, students do not know their own remedial remedial work.

normal loader & pitch loader

There are four types of Loaders, and the execution sequence of the four types of loaders is briefly mentioned. here We will elaborate on the execution sequence of the four Loaders.

What is it brieflynormalandpitch

The normal Loader is essentially the Loader function itself.

// The loader function itself is called the normal phase of the loader
function loader(source) {
    // dosomething
    return source
}
Copy the code

Pitch loader is a function of the pitch attribute on the Normal loader:

// Pitch Loader is a property on normal
loader.pitch = function (remainingRequest,previousRequest,data) {
    // ...
}
Copy the code

This is pitch loader and Normal Loader in a nutshell.

We will beloaderthepitchAttribute is calledloaderthepitch loader.

Naturally, we willloaderThe function itself is callednoram loader.

If you are not clear about the pitch loader and normal Loader parameters and what the return values represent. It is highly recommended that you first read Webpack5 Loader principles from multiple perspectives.

Execution phase

I took care of the students with weak foundation and talked a little about the basic content of PITCH and normal. Then what are the functions of these two stages?

Here we use a graph to see the corresponding execution order:

There are 8 Loaders in the figure, and they have corresponding types respectively. The introduction of a file in Webpack will first enter the stage of loader processing file, and then it will be handed to WebPack for compilation after loader processing is completed.

From the picture above, we can see:

  • A resource is first introduced in a Webpack (either by import or require) and is first entered into the Loader processing phase.

  • In the loader processing stage, the function call of pitch Loader will be performed from left to right, layer by layer. The processing order is POST, inline, normal, and pre.

  • This step will read the content of the imported resource file only after the pitch phase has completed all processing.

  • The read resource file content is handed to the NorAML-loader function and passed layer by layer. The execution order is pre, normal, inline, and POST.

  • Finally, the resources processed by loader are returned to Webpack for compilation.

pitch loaderThe fusing effect of

As mentioned above, webpack will first go through the processing of Loader when compiling resources, which will go through two stages: pitch and normal stage respectively.

Here’s why the pitch stage exists and what the pitch stage is for. I will discuss this with you later in practice. First of all, we need to be clear about one important feature of pitch:

If there is a non-undefeind return value in pitch loader, the circuit breaker effect will occur in the whole loader chain in the figure above.

In case you’re wondering what a fuse breaker is, take a look at this picture:

If we return a string of 19Qingfeng in the pitch phase of the inline-loader, the loader will execute out of order.

It will turn around immediatelypitchThe return value of the function to execute the precedingnoraml loader.

Here are two additional notes:

  1. The non-undefeind value returned by the pitch phase causes the Loader to run out of order, which is called the circuit breaker effect.

  2. In normal execution, the resource file will be read and handed to the Normal Loader for processing, but the file will not be read if the pitch has a return value. The value returned by the PITCH function is given to the normal Loader that will execute.

Here you just need to understand the meaning of the so-called circuit breaker in pitch, and I’ll take you through the application scenarios.

Application source code analysis from the open source Loader

Congratulations to all of you here, let’s explore what most developers know but don’t know — when to design a Loader as a pitch Loader!

fromstyle-loaderStart from the source code

Here we first look at the style-loader source code:

You can take a quick look at the picture, there’s no need to dig into it. Trust me, just a quick swipe.

Emm… It does have long, smelly code, huh? Haha!

I’m not going to take you through the code here, because reading the full source doesn’t help much with what this article is trying to say.

But here’s what this “stinky, long” code does:

  • First of all, thisloaderAll logic is designed inpitchPhase carries out its executionnormalA function is an empty function.

  • Second,style-loaderWhat it does is simple: it gets the corresponding style file content and creates it on the pagestyleNode. Assigns the style content tostyleNodes then add nodesheadLabels are ok.

That’s easy, isn’t it? Get rid of any doubts you have about the pitch. Forget it! Let’s do it.

implementationstyle-loaderThe core logic

The real style-loader source code is only compatible with boundary cases, such as deciding whether you are using ESM or CJS, etc.

Here I want to emphasize with you is the source process, after all, a style-loader complete implementation I believe for everyone a little bit of effort can see it.

function styleLoader(source) {
  const script = `
    const styleEl = document.createElement('style')
    styleEl.innerHTML = The ${JSON.stringify(source)}
    document.head.appendChild(styleEl)
  `;
  return script;
}
Copy the code

Very simple, here we have implemented a style-loader function through the core ideas mentioned above, and finally exported a script script.

When webpack parses to the require(*.css) file, it passes the style-loader to wrap the returned script into a module.

Through therequire(*.css)The corresponding page will be added after executionstyleThe node.

If you are interested, you can build a Webpack environment for verification and hand over the files at the end of CSS to our style-loader.

If you are careful, you may have noticed that we put style-loader logic in normal phase and source code in pitch phase.

Is it ok to put it in the normal phase? Now, let’s write it a different way.

willstyle-loaderDesign becomenormal loader

Usually when we use style-loader to process our CSS style file, we will work with CSS-loader to process the import statement in the CSS file.

The style file will be processed by CSS-loader before being handed to style-loader.

Here, let’s use our own style-loader in conjunction with CSS-loader to handle it:

yarn add -D css-loader
Copy the code
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js'.devtool: false.mode: 'development'.output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',},resolveLoader: {
    modules: [path.resolve(__dirname, './loaders'), 'node_modules'],},module: {
    rules: [{test: /\.css$/,
        use: ['style-loader'.'css-loader'],},],},plugins: [new HtmlWebpackPlugin()],
};
Copy the code

At the same time, there are three business files in my directory:

  • src/index.js: Entry file for this package.
// What it does is simply introduce index.css
const styles = require('./index.css');
Copy the code
  • src/index.css: the entryjsFile to import the style file.
// index.cssDefined in thebodyBackground color // and pass@importThe./require.css @import url('./require.css');
body {
  color: red;
}
Copy the code
  • src/require.css:index.cssThe imported style file.
div {
  color: blue;
}
Copy the code

This is my own WebPack configuration file that uses the style-loader we just implemented and the original CSS-Loader to handle file style files.

Run the package again to open the generated HTML page:

We can see that the color:red we set on the body is missing.

Essentially, the reason for this problem is that the normal phase of CSS-loader processes the style file into a JS script and returns it to the normal function of style-loader.

We can print this in our style-loader:

function styleLoader(source) {
  console.log(source, 'source');
  const script = `
    const styleEl = document.createElement('style')
    styleEl.innerHTML = The ${JSON.stringify(source)}
    document.head.appendChild(styleEl)
  `;
  return script;
}
Copy the code

The source content is a JS script, and we insert the js script content into styleEl, of course, without any style effect.

This is the package that generates the STYLE node in HTML.

This means that if we design the style-loader as normal loader, we need to execute the JS script returned by the previous loader and obtain the exported content to get the corresponding style content.

In this case, we need to implement a series of JS methods in the normal phase of style-loader in order to execute JS and read the style content returned by CSS-Loader, which is a very bad design pattern.

willstyle-loaderDesign becomepitch loader

So, what if we try to design as pitch Loader according to the source code?

What good would that do? Let’s analyze it first.

First, if we return values directly in the pitch stage of the style-loader, the circuit breaker effect will occur.

As we mentioned above, normal loader will be executed immediately if the fusing effect occurs, because style-loader is the first process to be executed, equivalent to:

So why do it?

We can do it atstyle-loaderthepitchPhase throughrequireStatement to introducecss-loaderReturned after processing the filejsScript to get the exported results. And then reassemble the logic back towebpackCan.

The advantage of this is that the JS statements that execute csS-loader returns that we had to deal with earlier in the Normal phase do not need to implement the js execution logic at all. It’s all up to WebPack to execute.

Maybe most of you still don’t know what that means, but that’s okay. Let me take you through the basics:

function styleLoader(source) {}

/ / pitch period
styleLoader.pitch = function (remainingRequest, previousRequest, data) {
  const script = `
  import style from "!!${remainingRequest}"

    const styleEl = document.createElement('style')
    styleEl.innerHTML = style
    document.head.appendChild(styleEl)
  `;
  return script;
};

module.exports = styleLoader
Copy the code

Here I put the style-loader processing in the pitch stage.

RemainingRequest in pitch stage indicates that the absolute path of the remaining unprocessed loader is “!” Concatenates a string containing the resource path.

Here we return the js script directly in the style-loader pitch stage:

Webpack then compiles the JS script returned by style-loader.

Compile the returned script as a module, and compile the returned JS script recursively at the same time. Detect the presence of module introduction statement import/require for recursive compilation.

The module returned from the style-loader contains the following code:

 import style from "!!!!! ${remainingRequest}"
Copy the code

The tricky question we had in the Normal Loader phase about the CSS-loader return value being a JS script we gave webPack to compile through the import statement.

Webpack will import style from “!! ${remainingRequest}” Recompile is called another Module, when we run the compiled code:

  • The first analysisconst styles = require('./index.css');.style-loader pitchTo deal with./index.cssAnd return a script.

Webpack calls the compilation of the returned JS script a module and analyzes the dependent statements in the module for recursive compilation.

  • Due to thestyle-loader pitchPhase return exists in the scriptimportStatement, then at this timewebpackIt’s going to compile recursivelyimportStatement path module.

When webpack recursively compiles style-loader to return import statements from scripts, we will import style from “!! ${remainingRequest}”, in the script stage returned by style-loader pitch, get the JS script returned by CSS-loader, execute it and get its export content.

  • One thing to emphasize here is that we are usingimportStatement is used!!!!!(double exclamation mark) splicingremainingRequest, indicates that there is onlyinline loaderTo take effect. Otherwise it will cause an endless loop.

Now repackage the code and let’s see what the page looks like:

When you open the generated page, you’ll see that our style is back in effect.

Let’s take another look at the packaged JS code:

/ SRC /index.css is compiled as a module, and its contents are returned in the style-loader pitch phase.

Also inside this module you find another module by __webpack_require__, which is essentially import style from “!! ${remainingRequest}” ${remainingRequest}”

This is import style from “! ${remainingRequest}” statement ${remainingRequest} module compiled module code, here is only a partial screenshot.

We just need to see that the corresponding remainingRequest is also compiled into a Module ~

This is why style-loader implements pitch phase for logical processing of content.

We could have used normal if we had to, but we had to deal with too many import/require to implement module introductions, which is definitely a bad design pattern.

If you are interested in the webpack compilation process, you can check out this Webapck5 core packaging principle process analysis -300 lines of code to implement the core principles of Webpack.

Summary of actual Pitch application scenarios

From the style-loader example above, pitch is undoubtedly the best design method when we want to use the loader on the left in parallel.

Import someThing from pitch Loader! ${remainingRequest} Remaining loader, so that the return value of the last loader is JS script, script will be handed to Webpack to compile and execute, this is the actual application scenario of PITCH Loader.

Simply put, if you need to rely on other Loaders during loader development, and the last loader normal function returns a JAVASCRIPT script instead of the processed resource file content, then it is better to design your Loader logic in pitch stage.

Write at the end of the article

At the end of the article, I hope to talk with you a little bit about why I singled out an uncommon pitch Loader for a long talk.

First of all, I would like to thank everyone who can see the end. From my personal point of view, it is precisely the ability to control details that is the necessary quality for a senior software engineer.

While most people are stuck with concepts and basic meanings, you can walk the walk thinking about the best way to design your app in different application scenarios, even if it’s not often used.

But in my opinion, the ability to control the depth of knowledge and the ability to understand applications is the inherent reflection that determines the ceiling height of a software developer

Finally, I hope you can understand the meaning of loader pitch stage design and when you should consider using pitch to design your loader.