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 :
- 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" }],
},
};
- 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 substitutionloader-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 :
- Deal with from webpack Received context, Continue to add the necessary properties and helper methods .
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
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 .