On the mechanism of webpack loader

HZFEStudio 2021-10-13 14:59:50
mechanism webpack loader


Complete high frequency question bank warehouse address :https://github.com/hzfe/awesome-interview

Complete high frequency question bank reading address :https://febook.hzfe.org/

Related issues

  • webpack loader How it works
  • How to write webpack loader

Answer key points

transformation Life cycle chunk

webpack It can only be dealt with by itself JavaScript and JSON file , and loader by webpack Added the ability to handle other types of files .loader Convert other types of files to valid webpack modules( Such as ESmodule、CommonJS、AMD),webpack Can consume these modules , And add it to the dependency diagram .

loader It's essentially a function , This function converts the received content , Returns the result of the transformation .

common loader Yes :

  • raw-loader: Load the original contents of the file .
  • file-loader: Export the reference file to the destination folder , Reference the output file through a relative path in the code .
  • url-loader: and file-loader similar , But in the case of a very small file base64 Inject the contents of the file into the code .
  • babel-loader: take ES The newer syntax is converted to a browser compatible Syntax .
  • style-loader: take CSS Code injection into JavaScript in , adopt DOM Operation loading CSS.
  • css-loader: load CSS, Support for modularity 、 Compress 、 File import and other features .

Use loader There are two main ways :

  1. stay webpack.config.js Configuration in file , By means of module.rules Use in test Match the file type to be converted , Use use Specify the loader.
module.exports = {
module: {
rules: [{ test: /\.ts$/, use: "ts-loader" }],
},
};
  1. Inline use
import Styles from "style-loader!css-loader?modules!./styles.css";

In depth knowledge

1. To write webpack loader

1.1 Sync loader

After synchronizing the content , Can pass return Or call this.callback Return results .

export default function loader(content, map, meta) {
return someSyncOperation(content);
}

adopt this.callback You can return information other than content ( Such as sourcemap).

export default function loader(content, map, meta) {
this.callback(null, someSyncOperation(content), map, meta);
return; // When calling callback() when , Always return undefined
}

1.2 asynchronous loader

adopt this.async You can get the callback function of asynchronous operation , And return the result in the callback function .

export default function (content, map, meta) {
const callback = this.async();
someAsyncOperation(content, (err, result, sourceMaps, meta) => {
if (err) return callback(err);
callback(null, result, sourceMaps, meta);
});
}

Unless the calculation is small , Otherwise, for Node.js This single threaded environment , Use asynchronous... Whenever possible loader.

1.3 loader Develop auxiliary tools and tools loaderContext

loader-utils And schema-utils, You can pass access and validation to loader The work of the parameters is simplified .

import { getOptions } from "loader-utils";
import { validate } from "schema-utils";
const schema = {
type: "object",
properties: {
test: {
type: "string",
},
},
};
export default function (source) {
const options = getOptions(this);
validate(schema, options, {
name: "Example Loader",
baseDataPath: "options",
});
// Apply some transformations to the source...
return `export default ${JSON.stringify(source)}`;
}

loader-utils There are mainly the following tools and methods :

  • parseQuery: analysis loader Of query Parameters , Return an object .
  • stringifyRequest: Convert the requested resource into a resource that can be used in loader In the generated code require or import Relative path string used , At the same time, avoid recalculation caused by absolute path hash value .

    loaderUtils.stringifyRequest(this, "./test.js");
    // "\"./test.js\""
  • urlToRequest: Convert the requested resource path to webpack Forms that can be handled .

    const url = "~path/to/module.js";
    const request = loaderUtils.urlToRequest(url); // "path/to/module.js"
  • interpolateName: Interpolate the file name template .

    // loaderContext.resourcePath = "/absolute/path/to/app/js/hzfe.js"
    loaderUtils.interpolateName(loaderContext, "js/[hash].script.[ext]", { content: ... });
    // => js/9473fdd0d880a43c21b7778d34872157.script.js
  • getHashDigest: Get the contents of the file hash value .

Writing loader In the process of , It can also be used loaderContext Object to get loader And carry out some advanced operations , Common properties and methods are :

  • this.addDependency: Add a file , As loader The dependence of the resulting results , So that it can be monitored when there is any change , This triggers recompilation .
  • this.async: tell loader-runner This loader The callback will be executed asynchronously .
  • this.cacheable: By default , take loader The processing result of is marked as cacheable . Pass in false It can be turned off loader Cache ability to process results .
  • this.fs: Used to access the compilation Of inputFileSystem attribute .
  • this.getOptions: extract loader Configuration options . from webpack 5 Start , Can be obtained loader Context object , Used in substitution loader-utils Medium getOptions Method .
  • this.mode: webpack The mode of operation of , It can be "development" or "production".
  • this.query: If loader Configured with options object , Then point to this object . If loader No, options, But rather query String as argument ,query It is a question of ? Starting string .

2. webpack loader Working mechanism

2.1 according to module.rules analysis loader Load the rules

When webpack Handle a module (module) when , According to... In the configuration file module.rules The rules of , Use loader Processing corresponding resources , Get something to webpack The use of JavaScript modular .

According to the specific configuration ,loader There will be different types , Can affect loader Execution order of . The specific types are as follows :

rules: [
// pre In front of loader
{ enforce: "pre", test: /\.js$/, loader: "eslint-loader" },
// normal loader
{ test: /\.js$/, loader: "babel-loader" },
// post After loader
{ enforce: "post", test: /\.js$/, loader: "eslint-loader" },
];

And inline use inline loader:

import "style-loader!css-loader!sass-loader!./hzfe.scss";

In the normal execution process , These different types of loader The order of execution is :pre -> normal -> inline -> post. What will be mentioned in the next section pitch In the process , these loader The order of execution is the reverse :post -> inline -> normal -> pre.

For inline loader, You can notify the modifier prefix to change loader Execution order of :

// ! The prefix disables normal loader
import { HZFE } from "!./hzfe.js";
// -! The prefix disables pre loader and normal loader
import { HZFE } from "-!./hzfe.js";
// !! The prefix disables pre、normal and post loader
import { HZFE } from "!!./hzfe.js";

In general ,! The prefix and inline loader Used together only in loader( Such as style-loader) In the generated code ,webpack The official does not recommend that users use inline loader and ! Prefix .

webpack rules Configured in loader It can be multiple chain connected in series . In the normal process , The chain loader It will be executed from back to front .

  • final loader The first to perform , It receives a resource file (resource file) The content of .
  • first loader Finally, execute , It will return JavaScript Modules and optional source map.
  • In the middle loader, There are no specific requirements for receiving and returning , As long as you can handle it before loader What's coming back , Produce the next loader What you can understand .

2.2 loader-runner The implementation process of

webpack call loader The timing is triggering compilation Of buildModule After the hook .webpack Will be in NormalModule.js in , call runLoaders function loader:

runLoaders(
{
resource: this.resource, // Path to resource file , You can have a query string . Such as :'./test.txt?query'
loaders: this.loaders, // loader The path of .
context: loaderContext, // Pass to loader The context of
processResource: (loaderContext, resourcePath, callback) => {
// Access to resources , Yes scheme The document passed readResourceForScheme Read , Otherwise, by fs.readFile Read .
const resource = loaderContext.resource;
const scheme = getScheme(resource);
if (scheme) {
hooks.readResourceForScheme
.for(scheme)
.callAsync(resource, this, (err, result) => {
// ...
return callback(null, result);
});
} else {
loaderContext.addDependency(resourcePath);
fs.readFile(resourcePath, callback);
}
},
},
(err, result) => {
// When loader After the conversion , Will return the result to webpack Continue to deal with .
processResult(err, result.result);
}
);

runLoaders The function comes from loader-runner package . Introducing runLoaders Before the specific process , Let me introduce you pitch Stage , The method described in the previous section is carried out from back to front loader The process of , Generally called normal Stage . In contrast , There's another one called pitch Phase process .

One loader If in the exported function pitch Property is hung on the method , Then this method will be in pitch Stage execution .pitch Stage is different from normal Stage ,pitch The execution sequence of phases is from front to back , The whole process is similar to browser event model or onion model ,pitch The phase is carried out from front to back loader, And then into normal The phase is carried out from the back to the front loader. Be careful ,pitch Phases generally do not return values , once pitch There are stages loader Return value , Then start from here and go back to normal Stage .

loader-runner The specific process is as follows :

  1. Deal with from webpack Received context, Continue to add the necessary properties and helper methods .
  2. iteratePitchingLoaders Handle pitch loader.

    If we give one module There are three loader, Every loader They are equipped with pitch function :

    module.exports = {
    //...
    module: {
    rules: [
    {
    //...
    use: ["a-loader", "b-loader", "c-loader"],
    },
    ],
    },
    };

    So deal with this module The process is 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

    If b-loader stay pitch The value... Is returned in advance , So the process is as follows :

    |- a-loader `pitch`
    |- b-loader `pitch` returns a module
    |- a-loader normal execution
  3. iterateNormalLoaders Handle normal loader.

    When pitch loader After the process is processed , It's time to deal with normal loader The process of . Handle normal loader The process and pitch loader be similar , Just iterate back and forth .

    iterateNormalLoaders and iteratePitchingLoaders Will be called runSyncOrAsync To execute loader.runSyncOrAsync Would provide context.async, This is a return callback Of async function , For asynchronous processing .

3. common webpack loader Principle analysis

loader The operation itself is not complicated , Is the one responsible for converting other resources to JavaScript Function of module .

3.1 raw-loader analysis

The loader It is a very simple synchronization loader, Its core step is to get the serialized string from the original content of the file , Repair JSON When serializing special characters bug, Add export statement , Make it a JavaScript modular .

The loader stay webpack 5 Has been abandoned , Use it directly asset modules Can be replaced by . The loader Source code is as follows :

import { getOptions } from "loader-utils";
import { validate } from "schema-utils";
import schema from "./options.json";
export default function rawLoader(source) {
const options = getOptions(this);
validate(schema, options, {
name: "Raw Loader",
baseDataPath: "options",
});
const json = JSON.stringify(source)
.replace(/\u2028/g, "\\u2028")
.replace(/\u2029/g, "\\u2029");
const esModule =
typeof options.esModule !== "undefined" ? options.esModule : true;
return `${esModule ? "export default" : "module.exports ="} ${json};`;
}

3.2 babel-loader analysis

babel loader It is a combination of synchronous and asynchronous loader, Run in asynchronous mode when using cache configuration , Otherwise, run in synchronous mode . The loader The main source code is as follows :

// imports ...
// ...
const transpile = function (source, options) {
// ...
let result;
try {
result = babel.transform(source, options);
} catch (error) {
// ...
}
// ...
return {
code: code,
map: map,
metadata: metadata,
};
};
// ...
module.exports = function (source, inputSourceMap) {
// ...
if (cacheDirectory) {
const callback = this.async();
return cache(
{
directory: cacheDirectory,
identifier: cacheIdentifier,
source: source,
options: options,
transform: transpile,
},
(err, { code, map, metadata } = {}) => {
if (err) return callback(err);
metadataSubscribers.forEach((s) => passMetadata(s, this, metadata));
return callback(null, code, map);
}
);
}
const { code, map, metadata } = transpile(source, options);
this.callback(null, code, map);
};

babel-loader adopt callback Passed on the past babel.transform Converted code and source map.

3.3 style-loader And css-loader analysis

style-loader Responsible for inserting styles into DOM in , Make the style effective on the page .css-loader Mainly responsible for handling import、url External references such as paths .

style-loader Only pitch function .css-loader yes normal module. The whole execution process is to execute style-loader Stage ,style-loader Will create a shape such as require(!!./hzfe.css) The code returned to webpack.webpack Will call... Again css-loader Handling styles ,css-loader Will return a message containing runtime Of js Module to webpack Parse .style-loader Inject... In the previous step require(!!./hzfe.css) At the same time , Also injected with the addition of style Code of label . such , At run time ( Browser ),style-loader You can put the css-loader Insert a new style into the page .

The common question is why not follow normal Model organization style-loader and css-loader.

First css-loader The return is a code like this :

import ___CSS_LOADER_API_IMPORT___ from "../node_modules/[email protected]@css-loader/dist/runtime/api.js";
var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(function (i) {
return i[1];
});
// Module
___CSS_LOADER_EXPORT___.push([
module.id,
".hzfe{\r\n height: 100px;\r\n}",
"",
]);
// Exports
export default ___CSS_LOADER_EXPORT___;

style-loader Cannot get at compile time CSS Related content , because style-loader Unable to deal with css-loader Generate the result runtime rely on .style-loader You can't get... At run time CSS Related content , Because no matter how you splice the runtime code , Can't get CSS The content of .

As a substitute ,style-loader Adopted pitch programme ,style-loader The core functions of are as follows :

module.exports.pitch = function (request) {
var result = [
// Generate require CSS File statement , hand css-loader analysis Get contained CSS Content JS modular
// among !! To avoid webpack Recursively call... When parsing style-loader
`var content=require("${loaderUtils.stringifyRequest(this, `!!${request}`)}")`,
// Call... At run time addStyle hold CSS Content inserted into DOM in
`require("${loaderUtils.stringifyRequest(this, `!${path.join(__dirname, "add-style.js")}`)}")(content)`
// If it is found that CSS modules, Export it by default
"if(content.locals) module.exports = content.locals",
];
return result.join(";");
};
module.exports = function (content) {
var style = document.createElement("style");
style.innerHTML = content;
document.head.appendChild(style);
};

stay pitch Stage ,style-loader Generate require CSS And inject runtime Code for . The result will be returned to webpack Further analysis ,css-loader The returned results are imported as modules at run time , Available at runtime CSS The content of , And then call add-style.js hold CSS Content inserted into DOM in .

Reference material

  1. writting a loader
  2. Loader Interface
  3. loader runner
版权声明
本文为[HZFEStudio]所创,转载请带上原文链接,感谢
https://qdmana.com/2021/10/20211001192624290Z.html

  1. Projet Java: système de gestion du rendement des employés (Java + SSM + MySQL + Maven + HTML)
  2. CSS tips | one line of code to realize the integration of avatar and national flag
  3. Maotai and Paris Fashion Week joined hands to make Chinese elements appear on the show
  4. Wang Xiaoya showed up in a sleeveless skirt and reappeared her intellectual elegance. She was still full of temperament after leaving the nest CCTV
  5. Comment écrire un document de conception frontale
  6. Créer une api javascript haute performance avec Rust et l'exécuter dans webassembly
  7. Analyse de certains principes techniques clés du SDK de surveillance frontale
  8. Point de vue: la NFT de type portrait a formé un modèle d'entreprise. Quelles sont ses perspectives d'avenir et ses difficultés?
  9. Stars celebrate the motherland's birthday in patterns: Tang Yan Bixin, Liu Xiaoqing in military uniform, Zhao Liying and he Jiong send blessings
  10. L'amour entre Wing Mei et Luan Tree: de l'amour à première vue à l'amour éternel
  11. Disappeared car companies: tape measure is useless? Zhongtai is on the verge of death and has no way to return to heaven
  12. BUUCTF [极客大挑战 2019]Http
  13. element缓存到本地使用
  14. How can the volunteer army with less steel and more gas beat the American army with more steel and less gas? Changjin Lake gives you the answer
  15. CentOS installation source package nginx error
  16. Mise en cache des éléments pour utilisation locale
  17. Disappeared car companies: tape measure is useless? Zhongtai is on the verge of death and has no way to return to heaven
  18. He saifei est si naturel!58 ans est si beau, porter des vêtements de vieillesse pour accepter de vieillir!
  19. Finally pregnant! Seven years of pregnancy, collective blessing of the entertainment industry
  20. Wu Qili sent blessings on the national day. She looked haggard and thin on her own. It was worrying
  21. Error in spring source code compilation: the monoprocessor in reactor.core.publisher is outdated
  22. Buctf [geek Challenge 2019] http
  23. 20 个值得学习的 Vue 开源项目
  24. 20 projets open source à apprendre
  25. Scène et application de la manette des gaz anti - bavardage
  26. Qu'est - ce que j'ai gagné en abandonnant vue pour les six mois de React?
  27. À partir de [Bytecode cache] et de [http cache], intervieweur: « est - ce si mince? »
  28. [niveau intermédiaire et avancé] obligatoire, 30 + questions manuscrites à haute fréquence et réponses détaillées (dix mille caractères longs), voyez comment "vous" ne pouvez pas m'abattre
  29. Mise en œuvre d'un outil d'échafaudage universel pour l'ingénierie Web de 0 à 1
  30. 【中高级前端】必备,30+高频手写题及详细答案(万字长文),看“你”怎么难倒我
  31. In less than two days, the box office exceeded 400 million, and Changjin Lake broke out, breaking seven records in Chinese film history
  32. Tong Liya Jin Chen bumps her hair, Xie Na Zhao Liying bumps her shirt, and she sees EQ from the reaction
  33. react之组件生命周期
  34. L'équipe de vue dévoile un nouvel outil d'échafaudage rapide comme la foudre Create View, qui remplacera la vue CLI à l'avenir, avec seulement 300 lignes de code, apprenez - le!
  35. 20 dessins illustrant le fonctionnement du moteur de rendu du Navigateur
  36. In less than two days, the box office exceeded 400 million, and Changjin Lake broke out, breaking seven records in Chinese film history
  37. 千锋重庆web前端学习之理解CSS位置属性
  38. 什么是语义HTML标记以及如何使用它们?
  39. Si vous vous représentez avec un tableau, c'est le champ de Van Gogh.
  40. 前端面试手写代码
  41. 前端开发框架Vue中Vuex的使用原理分享
  42. vue-echarts初次体验
  43. 分享一些web前端开发好用的网站
  44. 每天读一点webpack-003
  45. react之组件生命周期
  46. Alibaba collection version of mybatis handwritten documents, Java front-end interview questions
  47. SpringBoot Java后端实现okhttp3超时设置
  48. react之組件生命周期
  49. Cycle de vie des composants de React
  50. 使用Reactor将阻塞调用变为异步非阻塞
  51. Baked cake wife sun photos, plain face on camera, beautiful appearance is still a beauty, watching children during the festival is a little helpless
  52. 亚洲知名插画师荒川(arakawa) 仅8件独版NFT作品系列《Can't Out》正式上架Element综合市场
  53. Taiyuan: singing, welcoming the national day, gathering to praise blessings
  54. Arakawa, un illustrateur Asiatique bien connu, n'a mis sur le marché que huit pièces de la collection NFT "can't out" en une seule édition.
  55. Résumé des questions d'entrevue Hadoop (II) - - hdfs
  56. 如何解决“Serverless”系统的冷启动问题
  57. BootstrapBlazor 模板安装
  58. BootstrapBlazor 模板安装
  59. Tong Liya Jin Chen bumps her hair, Xie Na Zhao Liying bumps her shirt, and she sees EQ from the reaction
  60. 使用ESLint+Prettier来统一前端代码风格