preface
In the in-depth analysis of Loader in the previous article [Webpack Advanced], the principle and operation mechanism of Loader was explored. This article will deepen the understanding of Loader by introducing the functions and implementation of common styles of Loader.
For a style file (less as an example), the most common Loader configuration is:
{
module: {
rules: [{test: /\.less$/,
use: [
'style-loader'.'css-loader'.'less-loader',],},],},}Copy the code
Let’s take a look at each one.
less-loader
Less is a CSS preprocessing language, extending the CSS language, adding variables, mixins, functions and other features. Less-loader is used to translate Less code into CSS code that can be recognized by the browser.
// demo.less
@base: #f938ab;
.box-shadow(@style.@c) when (iscolor(@c)) {
-webkit-box-shadow: @style @c;
box-shadow: @style @c;
}
.box-shadow(@style.@alpha: 50%) when (isnumber(@alpha)) {
.box-shadow(@style, rgba(0.0.0.@alpha));
}
.box {
color: saturate(@base.5%);
border-color: lighten(@base.30%);
div { .box-shadow(0 0 5px.30%)}}Copy the code
The above less code will be translated by less-loader to:
// demo.css
.box {
color: #fe33ac;
border-color: #fdcdea;
}
.box div {
-webkit-box-shadow: 0 0 5px rgba(0.0.0.0.3);
box-shadow: 0 0 5px rgba(0.0.0.0.3);
}
Copy the code
Therefore, the principle of less-loader is very simple, which is to call the methods provided by the less library, translate the less syntax and output it as follows:
// Less-loader implementation (simplified)
const less = require('less');
module.exports = function(content) {
const callback = this.async(); // Translation is time-consuming and asynchronous
const options = this.getOptions(); // Obtain the options of less-loader in the configuration file
less.render(
content,
createOptions(options), // less translation configuration
(err, output) = > {
callback(err, output.css); // Pass the generated CSS code to the next loader}); };Copy the code
css-loader
The purpose of CSS-loader is to parse @import and URL statements in Css files, process CSS-modules, and return the result as a JS module.
If we have a.c. S, b.c. S, c.
// a.css
@import './b.css'; / / import biggest ss
.a {
font-size: 16px;
}
// b.css
@import './c.css'; / / import Arthur c. ss
.b {
color: red;
}
// c.css
.c {
font-weight: bolder;
}
Copy the code
Cs-loader () ¶
/ / CSS - loader output
exports = module.exports = require(".. /.. /.. /node_modules/css-loader/lib/css-base.js") (false);
// imports
// The file needs to rely on the JS module, which is empty
// module
exports.push([ // The module exports the content
module.id,
".src-components-Home-index__c--3riXS {\n font-weight: bolder;\n}\n.src-components-Home-index__b--I-yI3 {\n color: red;\n}\n.src-components-Home-index__a--3EFPE {\n font-size: 16px;\n}\n".""
]);
// exports
exports.locals = { // csS-modules class name mapping
"c": "src-components-Home-index__c--3riXS"."b": "src-components-Home-index__b--I-yI3"."a": "src-components-Home-index__a--3EFPE"
};
Copy the code
It can be understood that CSS-loader concatenates the style content of A. CSS, B. CSS and C. CSS together in the form of strings and exports them as the content of JS modules.
// cS-loader source code (simplified)
// https://github.com/webpack-contrib/css-loader/blob/master/src/index.js
import postcss from 'postcss';
module.exports = async function (content, map, meta) {
const options = this.getOptions(); // Get the configuration
const plugins = []; // PostCSS plugin to translate source code
shouldUseModulesPlugins(options, this) && plugins.push(modulesPlugins); / / handle CSS - modules
shouldUseImportPlugin(options, this) && plugins.push(importPlugin); // process the @import statement
shouldUseURLPlugin(options, this) && plugins.push(urlPlugin); // process the URL () statement
shouldUseIcssPlugin(options, this) && plugins.push(icssPlugin); // Handle icSS related logic
if (meta && meta.ast) { // Reuse CSS AST generated by previous loader (e.g. Postcss-loader)
content = meta.ast.root;
}
const result = await postcss(plugins).process(content); // Use postcss to translate the source code
const importCode = getImportCode(); // Dependency statements to import
const moduleCode = getModuleCode(result); // The module exports the content
const exportCode = getExportCode(); // Other information that needs to be exported, such as csS-modules class name mapping, etc
const callback = this.async(); // Return asynchronously
callback(null.`${importCode}${moduleCode}${exportCode}`);
};
Copy the code
style-loader
We have got the complete CSS style code after the csS-loader translation. The style-loader is used to insert the result into the DOM tree as a style tag.
It seems intuitive that we just need to return js code like this and insert the result returned by csS-loader into the DOM:
module.exports = function (content) {
return `
const style = document.createElement('style');
style.innerHTML = '${content}';
document.head.appendChild(style);
`;
};
Copy the code
However, csS-loader does not return the text of CSS style code, but a JS module code, the js code directly into the style label is obviously not.
Let’s look at the style-loader implementation:
// style-loader
import loaderUtils from 'loader-utils';
module.exports = function (content) {
// do nothing
};
module.exports.pitch = function (remainingRequest) {
/* * Use the require statement to obtain the export of the JS module returned by csS-loader. The 'prefix skips the loader in the configuration to avoid repeating the * remainingRequest parameter to obtain the rest of the loader chain, In this case csS-loader, less-loader * uses the stringifyRequest method of loaderUtils to convert the absolute path in the request statement to a relative path */
const requestPath = loaderUtils.stringifyRequest(this.'!!!!! ' + remainingRequest);
// In this case, the requestPath is:
/ / '!!!!! . /node_modules/css-loader/index.js! . /node_modules/less-loader/dist/cjs.js! src/styles/index.less'
return `
const content = require(${requestPath})
const style = document.createElement('style');
style.innerHTML = content;
document.head.appendChild(style);
`;
};
Copy the code
Design of style-loader
- CSS -loader returns styles that can only be obtained from the runtime of its JS module, so use
require
Statements made - The normal method actually does nothing in the pitch method, right
Interrupts the execution of the Loader chain
, and then inline calls the back loader to load the current less file - Putting the implementation in pitch into the normal method causes the Loader chain to execute twice
- If there is no ‘!! The ‘prefix causes the loader chain to be called in an infinite loop
Style-loader implementation logic is quite round, is also a classic pitch application, understand its principle, can be said to be loader call chain, execution sequence and modular output have a more comprehensive understanding, recommended to experience.