Write loaders and plug-ins

Write the loader

One of the simplest loader code structures

  • Definition: Loader is simply a JavaScript module exported as a function
    module.exports = function(source) {
        return source;
    };
    Copy the code

Order of execution for multiple Loaders

  • Multiple Loaders are executed consecutively
  • From the back to the front
module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    }, 
    module: {
         rules: [
             {
                 test: /\.less$/,
                 use:[
                 'style-loader', 
                 'css-loader', 
                 'less-loader'
                 ]
             }
         ]
     }
};
Copy the code

Two cases of combination of functions

  • In the Unix pipline
  • Compose(Webpack takes this one)
    compose = (f, g) => (... args) => f(g(... args));Copy the code

Loader – runner is introduced

  • Definition: Loader-Runner allows you to run loaders without installing WebPack
  • Function:
    • As a dependency on WebPack, it is used in WebPack to execute the Loader
    • Develop and debug loader

The use of loader – runner

import { runLoaders } from "loader-runner"; RunLoaders ({resource: "/ abs/path/to/file. TXT? Loaders: [" /abs/path/to/loader.js? Query "], // String[]: The absolute path of the loader (you can increase the query string) context: {minimize: true}, // extra Loader context readResource outside the underlying context: Function (err, result) {// err: Error? // result.result: Buffer | String })Copy the code

Develop a raw-loader

src/raw-loader.js: Module. exports = function(source) {const json = json.stringify (source).replace(/\u2028/g, '\u2028') // For safety, Replace (/\u2029/g, '\\u2029'); return `export default ${json}`; }; src/demo.txt foobarCopy the code

Debug loader using loader-runner

run-loader.js: const fs = require("fs"); const path = require("path"); const { runLoaders } = require("loader-runner"); runLoaders( { resource: "./demo.txt", loaders: [path.resolve(__dirname, "./loaders/rawloader")], readResource: fs.readFile.bind(fs), }, (err, result) => (err ? console.error(err) : console.log(result)) ); Run the command: node run-loader.jsCopy the code

Loader parameter

  • Obtain the value using the getOptions method of Loader-utils
const loaderUtils = require("loader-utils");
module.exports = function(content) {
    const { name } = loaderUtils.getOptions(this);
};
Copy the code

Loader Exception Handling

  • Thrown directly by the loader
  • An error is passed through this.callback
this.callback( err: Error | null, content: string | Buffer, sourceMap? : SourceMap, meta? : any );Copy the code

Asynchronous processing of loader

  • Return an asynchronous function via this.async
    • The first argument is Error, and the second argument is the result of the processing
    module.exports = function(input) {
        const callback = this.async();
        // No callback -> return synchronous results
        // if (callback) { ... }
        callback(null, input + input);
    };
    Copy the code

Use the cache in loader

  • Loader caching is enabled in WebPack by default
    • You can turn off caching by using this.cacheable(false)
  • Cache condition: The loader result has a definite output under the same input
    • The dependent Loader cannot use the cache

How does Loader output files?

  • File writing is done through this.emitfile
const loaderUtils = require("loader-utils"); module.exports = function(content) { const url = loaderUtils.interpolateName(this, "[hash].[ext]", { content, }); this.emitFile(url, content); const path = `__webpack_public_path__ + ${JSON.stringify(url)}; `; return `export default ${path}`; };Copy the code

Practical development of an automatic synthesis of Sprite diagram loader

  • Supported syntax:
  • background: url(‘a.png? __sprite’);
  • background: url(‘b.png? __sprite’);

Converted to

  • background: url(‘sprite.png’);

How do I combine two images into one?

Use spritesmith spritesmith (https://www.npmjs.com/package/spritesmith) using the sample const sprites = ['.. / images / 1. JPG ', './images/2.jpg']; Spritesmith.run({src: sprites}, function handleResult (err, result) { result.image; result.coordinates; result.properties; });Copy the code

Warehouse address: github.com/glihui/my-l…

Writing a plug-in

The environment in which the plug-in runs

  • Plug-ins do not have a stand-alone runtime environment like Loader
  • Run only in WebPack

The basic structure of the plug-in

  • Basic structure:
    Class MyPlugin {// Plugin name apply(compiler) {// Plugin's apply method is compiler.hooks.dose.tap (' MyPlugin ', 'MyPlugin'); */) => {console.log('Hello World! '); // Plugin processing logic}); } } module.exports = MyPlugin;Copy the code
  • Plug-in use:
plugins: [ new MyPlugin() ]
Copy the code

Build the running environment of the plug-in

const path = require("path");
const DemoPlugin = require("./plugins/demo-plugin.js");
const PATHS = {
    lib: path.join(__dirname, "app", "shake.js"),
    build:path.join(__dirname, "build"), 
};
module.exports = {
    entry: {
        lib: PATHS.lib, 
    },
    output: {
        path: PATHS.build, 
        filename: "[name].js", 
    },
    plugins: [new DemoPlugin()], 
};
Copy the code

Develop a simple plug-in

src/demo-plugin.js module.exports = class DemoPlugin { constructor(options) { this.options = options; } apply() { console.log("apply", this.options); }}; // exports = {... plugins: [new DemoPlugin({ name: "demo" })] };Copy the code

How do I get passed parameters in the plug-in?

  • Through the plug-in’s constructor
module.exports = class MyPlugin { constructor(options) { this.options = options; } apply() { console.log("apply", this.options); }};Copy the code

Error handling for plug-ins

  • Parameter verification can be thrown directly in the form of throw
    • Throw new Error(” Error Message “)
  • Warnings and errors are accepted through the Compilation object
    • compilation.warnings.push(“warning”);
    • compilation.errors.push(“error”);

File writing is performed through Compilation

  • Assets on Compilation can be used for file writing
    • · You can set the ZIP resource bundle to the Compilation. assets object
    • File is written to use webpack – sources (www.npmjs.com/package/web… sources)
    const { RawSource } = require("webpack-sources"); module.exports = class DemoPlugin { constructor(options) { this.options = options; } apply(compiler) { const { name } = this.options; compiler.hooks.emit.tapAsync("DemoPlugin", (compilation, cb) => { compilation.assets[name] = new RawSource("demo"); cb(); }); }};Copy the code

Plug-in extensions: Write plug-ins for plug-ins

  • The plug-in itself can also be extended by exposing hooks, as in the case of the HTmL-Webpack-plugin
    , HTML - webpack - plugin - alter - chunks (Sync), HTML - webpack - plugin - before - HTML - generation (Async) , HTML - webpack - plugin - alter - asset - tags (Async) HTML - webpack - plugin - after - HTML - processing (Async) , HTML - webpack - plugin - after - emit (Async)Copy the code

Write a plug-in that compresses build resources into a ZIP package

  • Requirements:
    • · The name of the generated ZIP package file can be passed in through the plug-in
    • · Special hooks on the Compiler object are required for resource generation

Warehouse address: github.com/glihui/my-p…

Finally:

If you have a dream, you must come to the big city, here can help you achieve your dream

Some dreams need the power of cities to achieve

Links to other articles in this series

  • Webpack covers everything from the light to the deep, so that your resume is really “familiar” (Series 1)
  • Webpack covers everything from the light to the deep, so that your resume is really “familiar” (Series 2)
  • Webpack covers everything from the shallow to the deep, so that your resume really says “familiar” (Series 3)
  • Webpack covers everything from the shallow to the deep, so that your resume is really “familiar” (Series 4)
  • Webpack covers everything from the light to the deep, making your resume truly “familiar” (Series 5)