What is the Loader
Loader is a very important concept in Webpack. Loader can be simply understood as a file processor. Webpack uses Loader to process all kinds of files, such as.scSS into CSS files and base64 images.
Essentially, a loader is just a JavaScript module exported as a function that is called when Webpack is packaged to pass in the results or resource files generated by the previous Loader.
This time I plan to develop a Loader to convert ordinary images to.webp format to achieve the purpose of reducing the size of images.
Loader tool library
Before developing a Loader, learn about two toolkits for loader development – loader-utils and schema-utils
loader-utils
Used to obtain the loaderoptions
.schema-utils
Used to verify the Loaderoptions
与JSON Schema
Is the structure defined consistently?
import { getOptions } from 'loader-utils';
import { validateOptions } from 'schema-utils';
const schema = {
// ...
}
export default function(source) {
/ / get the options
const options = getOptions(this);
// Check whether loader options are valid
validateOptions(schema, options, 'Demo Loader');
// Write the conversion loader logic here
// ...
};
Copy the code
Loader development Criteria
- Single responsibility: A loader handles only one task, which is simple and maintainable. Complex scenarios can be completed by combining multiple Loaders.
- Modularity: Ensure that the Loader is modular. The generated modules of loader must comply with the same design principles as common modules.
- Stateless: We should not keep state in the Loader between module transitions. Each loader runtime should be independent of other compiled modules and should also be independent of previous loaders’ compilations of the same module.
In short, when we write a loader, we should keep its responsibilities simple and only care about the input and output.
Synchronous and asynchronous Loaders
Loaders are classified into synchronous and asynchronous because some resources are time-consuming to be processed asynchronously and can be executed after the processing is complete. Loader returns this.callback() as follows:
this.callback(
err: Error | null.content: string | Buffer, sourceMap? : SourceMap, meta? : any );Copy the code
- The first argument must be Error or NULL
- The second argument is a string or Buffer.
- Optional: The third parameter must be a source map that can be parsed by the module.
- Optional: The fourth option, which is ignored by Webpack, can be anything (such as some metadata).
// Synchronize loader returns multiple processing results
module.exports = function(content, map, meta) {
this.callback(null, someSyncOperation(content), map, meta);
};
Copy the code
In synchronous mode, if only one result is returned, you can use return to return the result.
// The synchronization loader returns only one processing result
module.exports = function(content, map, meta) {
return someSyncOperation(content);
};
Copy the code
This. Async is used to retrieve callback functions in asynchronous mode
module.exports = function(content, map, meta) {
var callback = this.async();
someAsyncOperation(content, function(err, result, sourceMaps, meta) {
if (err) return callback(err);
callback(null, result, sourceMaps, meta);
});
};
Copy the code
Develop your own Loader
Project directory structure
| – SRC | | – CJS. Js / / commonJs entrance | | – index. The js / / loader master file | | – options. The json / / loader options definition file | — Babel. Config. Js |–package.json |–readme.md
Define the options
The quality attribute is used to control the quality of the generated.webp file.
{
"additionalProperties": true."properties": {
"quality": {
"description": "quality factor (0:small.. 100:big), default=75"."type": "number"}},"type": "object"
}
Copy the code
Image format conversion
Cwebp this JS module provides ordinary pictures and. Webp between the conversion function.
const CWebp = require('cwebp').CWebp
/ * * * ordinary images. Webp image * @ param {string | buffer} img image absolute path or binary stream * @ param {number} quality generated webp image quality, The default is 75 * @returns. Webp file stream */
async function convertToWebp (img, quality = 75) {
let encoder = new CWebp(img)
encoder.quality = quality
let buffer = await encoder.toBuffer()
return buffer
}
Copy the code
Write the loader
import schema from './options.json'
export default async function loader (content) {
// Asynchronous mode
let callback = this.async()
/ / get the options
const options = loaderUtils.getOptions(this) | | {}/ / verification options
validateOptions(schema, options, {
name: 'webp Loader'.baseDataPath: 'options'
})
try {
// Go to.webp
let buffer = await convertToWebp(content, options.quality)
callback(null, buffer)
} catch (err) {
callback(err)
}
}
// loader receives file streams
export const raw = true
Copy the code
At this point the Loader is almost complete.
use
// webpack.config.js
module.exports = {
// Other configurations
// ...
module: {
rules: [{
test: /\.(png|jpg|gif)$/.use: [{
loader: 'file-loader'.options: {
name: '[name].[ext].webp'
}
},{
loader: 'webp-loader'.options: {
quality: 70}}]}}Copy the code
conclusion
In fact, writing a Loader is not as complicated as imagined, as long as you master the basic points, you can also have your own Loader.