Before I learned about the packaging and compilation principle of Webpack, today I saw part of the principle of Loader in Webpack, take this opportunity to share with you
What does loader do?
Loaders in Webpack are used to convert code, meaning that source code is converted from one type of code to another
Therefore, loader can be written as follows:
module.exports = function loader(code) {
// some transfrom operation... and return resultCode
return resultCode;
}
Copy the code
Basic Loader Configuration
The following is one of the basic configurations of loader in Webpack. If you do not know the configuration, you need to refer to the configuration rules of Loader in WebPack
module.exports = { ... .module: {
rules: [{test: /\.css/.// Regular expressions are used as matching rules for loader
use: [
{
loader: "".// Loader path, similar to the rule of require
options: { / / loader parameters. ,}}]}]}}Copy the code
Loader Execution sequence of packaging
We all know what loader is, so loader for the source code operation is in order, how to order? We can test the following code
// webpack.config.js
const path = require("path");
const outputPath = path.join(__dirname, "dist");
module.exports = {
mode: "development".entry: {
index: path.join(__dirname, "src"."index.js")},output: {
path: outputPath,
filename: "[name].js".clean: true
},
module: {
rules: [{test: /index.js$/,
use: [
"./loaders/loader1"."./loaders/loader2",]}, {test: /.js$/,
use: [
"./loaders/loader3"."./loaders/loader4",]}]}}// loaders/loader1.js
module.exports = function (code) {
console.log("loader1");
return code;
}
// loaders/loader2.js
module.exports = function (code) {
console.log("loader2");
return code;
}
// loaders/loader3.js
module.exports = function (code) {
console.log("loader3");
return code;
}
// loaders/loader4.js
module.exports = function (code) {
console.log("loader4");
return code;
}
Copy the code
After executing the command NPX webpack, the result is as follows:
The loader executes from back to front
SRC /index,js, SRC /a.js, SRC /a.js, SRC /a.js
// src/index.js
require("./a");
Copy the code
What is the output of the webpack loader?
Therefore, when the Loader of Webpack is executed, the import files are executed by Loader method first, and then the dependent files are executed by Loader method successively, and the dependencies are matched by Loader’s matching rules.
The sequence of loader packaging and AST syntax tree establishment
Change SRC /index.js to the following code
// src/index.js
require("./a"); Variables a =1;
Copy the code
NPX webpack cannot be packaged and an error is reported.
Next we modify the loader/loader4.js code as follows:
// loaders/loader4.js
module.exports = function (code) {
console.log("loader4");
const { changeVar } = this.getOptions();
const reg = new RegExp(changeVar, "g");
return code.replace(reg, "var");
}
Copy the code
Modify webpack.config.js loader configuration for loader4 as follows:
module.exports = { ... .module: {
rules: [..., {test: /.js$/,
use: [
"./loaders/loader3",
{
loader: "./loaders/loader4".// loader: converts source code to another source code to run before building the AST tree
options: {
changeVar: "Variable"}},]}]}}Copy the code
Then execute NPX webpack as follows:
Above we get the output and do not report an error. The execution sequence of loader packing is earlier than that of AST syntax tree, so the following figure shows the webPack packing process:
Some examples
Simple CSS – loader
The new loader/style – loader. Js
// loaders/style-loader
const path = require("path");
const loaderUtil = require("loader-utils");
module.exports = function (buffer) {
const {embed} = this.getOptions();
if (embed) {
return exportEmbedStyle(this, buffer);
}
return exportFileStyle(this, buffer);
}
function exportFileStyle(context, buffer) {
const {filePath, publicPath} = context.getOptions();
const filename = getFileName(context, buffer);
const fullPath = path.join(filePath, filename);
const relativePath = path.relative(publicPath, fullPath).replace(/\\/g."/");
return `const link = document.createElement("link");
link.rel = "stylesheet";
link.href = "${relativePath}";
document.head.appendChild(link);
module.exports = "${relativePath}"`;
}
function getFileName(context, buffer) {
const ext = path.extname(context.resourcePath);
const _name = path.basename(context.resourcePath);
const filename = _name.slice(0, _name.length - ext.length);
const hash = loaderUtil.interpolateName(context, "[contenthash]", {
content: buffer
});
const fullName = filename + "." + hash + ext;
context.emitFile(fullName, buffer);
return fullName;
}
function exportEmbedStyle(context, buffer) {
const code = buffer.toString();
return `const style = document.createElement("style"); style.innerHTML = `${code}`; document.head.appendChild(style); module.exports = `${code}` `;
}
module.exports.raw = true; // Let the source file be passed in as buffer
Copy the code
Modify the WebPack configuration:
const outputPath = path.join(__dirname, "dist");
const publicPath = path.join(__dirname, "public");
module.exports = { ... .module: {
rules: [..., {test: /.css$/,
use: [
{
loader: "./loaders/style-loader".options: {
filePath: outputPath,
publicPath: publicPath,
embed: false}}]}}Copy the code
Simple img – loader
New loaders/img – loader. Js
// loaders/img-loader.js
const path = require("path");
const loaderUtil = require("loader-utils");
function loader(buffer) {
console.log(this.context);
const { limit } = this.getOptions();
if (buffer.byteLength <= limit) {
return exportBase64(this, buffer);
}
return exportFile(this, buffer);
}
function exportFile(context, buffer) {
const filename = getFilePath(context, buffer);
const { filePath, publicPath } = context.getOptions();
const fullPath = path.join(filePath, filename);
const relativePath = path.relative(publicPath, fullPath).replace(/\\/g."/");
return `module.exports = "${relativePath}"`;
}
function exportBase64(context, buffer) {
const ext = path.extname(context.resourcePath).slice(1);
const content = base64Image(buffer, ext);
return `module.exports = `${content}` `;
}
function getFilePath(context, buffer) {
const ext = path.extname(context.resourcePath);
const _name = path.basename(context.resourcePath);
const filename = _name.slice(0, _name.length - ext.length);
const hash = loaderUtil.interpolateName(context, "[contenthash]", {
content: buffer
});
const fullName = filename + "." + hash + ext;
context.emitFile(fullName, buffer);
return fullName;
}
function base64Image(buffer, ext) {
let base64String = buffer.toString("base64");
return `data:image/${ext}; base64,${base64String}`;
}
loader.raw = true;
module.exports = loader;
Copy the code
Modify wenpack configuration:
const outputPath = path.join(__dirname, "dist");
const publicPath = path.join(__dirname, "public");
module.exports = { ... .module: {
rules: [..., {test: /.(png|jpg|gif)$/gi,
use: [
{
loader: "./loaders/img-loader".options: {
limit: 1024 * 100.// use the path for more than 10KB and base64 for less than 100KB
filePath: outputPath,
publicPath: publicPath
}
}
]
}
]
}
}
Copy the code
See more loader context: loader Interface | webpack
To sum up, this is my understanding and partial application of laoder packaging and modifying code. Welcome your comments