Author: JowayYoung warehouse: Github, CodePen blog: official website, nuggets, si Fu, Zhihu public number: IQ front special statement: the original is not easy, shall not be reproduced without authorization or plagiarism, if you need to be reproduced can contact the author authorized

preface

I have published a performance optimization article called “The Front-end Performance Optimization Guide,” which summarizes some of the performance optimization experiences used in the project development process. To be honest, performance optimizations may be useful during the interview process, but few students will actually pay attention to these details during the project development process.

If you pay close attention to the topic of performance tuning, you may find that no matter how best you optimize your code, you can’t compress an image once. Therefore, image compression has become the most common operation in performance optimization, whether manual compression image or automatic compression image, in the project development process must have.

Automatic compression of images is usually handled by some third-party Loader&Plugin plugged in during webPack build projects. Open Github and search for keywords such as webpack image. The most Star is image-webpack-loader and imagemin-webpack-plugin, which are Loader&Plugin. Many of you might choose them, they’re quick, they’re easy to use, they’re brainless.

However, there are special problems with the two Loader&plugins, both of which are developed based on Imagemin. Some of imagemin’s dependencies are hosted on foreign servers, and when YOU install them in NPM I XXX you default to GitHub Releases, which you can’t install if you’re not doing it properly, and you can’t install it if you’re doing it properly. So the author also published an article about NPM mirror processing “talk about NPM mirror those dangerous pit”, specifically to solve these problems caused by the network environment installation failure. In addition to this installation problem, Imagemin also has another big problem, that is, the loss of compression texture is more serious, the larger the image volume is more obvious, there are always a few compressed pictures are distorted, and the overall compression rate is not very high. This is likely to be caught by the careful QA sister when delivering the project, which is not clear compared to the design drawing.

tool

Image compression tool

At this point some students may have switched to manual compression of images. Better picture compression tools are nothing more than the following, if there is a better tool to add in the comments oh! At the same time, the author also sorted out their differences for your reference.

tool Open source charge API Free experience
QuickPicture ✖ ️ ✔ ️ ✖ ️ Compressible type more, compression texture is better, there are volume restrictions, there are quantity restrictions
ShrinkMe ✖ ️ ✖ ️ ✖ ️ Compressible type is more, the compression texture is general, there is no quantity limit, there is volume limit
Squoosh ✔ ️ ✖ ️ ✔ ️ Compressible type is less, the compression texture is general, there is no quantity limit, there is volume limit
TinyJpg ✖ ️ ✔ ️ ✔ ️ Compressible type is less, compression texture is very good, there are quantity limits, there are volume limits
TinyPng ✖ ️ ✔ ️ ✔ ️ Compressible type is less, compression texture is very good, there are quantity limits, there are volume limits
Zhitu ✖ ️ ✖ ️ ✖ ️ Compressible type is general, compression texture is general, there are quantity restrictions, there are volume restrictions

As can be seen from the above table comparison, free experience will have volume limitation, which is understandable, even charging is the same, after all, everyone upload a single picture of more than 10 meters, which server can tolerate. Again is the number of restrictions, one can only upload 20 pieces, as if there is a rule, compression texture is good on the number of restrictions, otherwise there is no limit on the number, of course, there is no limit after charging. Then comes the compressible type, the image type is JPG, PNG, GIF, SVG and webP, GIF compression will generally be distorted, SVG is usually used in vector ICONS rarely used in scene pictures, webP is rarely used due to compatibility problems, so JPG and PNG compression is enough. Of course, the compressed texture is the best consideration. To sum up, most students will choose TinyJpg and TinyPng. In fact, they are brothers from the same manufacturer.

A simple vote was held in a wechat discussion group on my official account, and TinyJpg and TinyPng won.

TinyJpg/TinyPng has a problem
  • Upload and download all depend onmanual
  • Can only be compressedjpgandpng
  • You can only compress each time20zhang
  • The maximum volume of each sheet cannot exceed5M
  • Visual processing information is not particularly complete
TinyJpg/TinyPng compression principle

TinyJpg/TinyPng uses smart lossy compression to reduce the size of images, selectively reducing similar colors in images, and saving data in very few bytes. The visual impact is almost invisible, but there is a big difference in file size. The use of intelligent lossy compression techniques is called quantization.

TinyJpg/TinyPng works better when compressing PNG files. Scan images for similar colors and merge them, convert 24-bit PNG files into smaller 8-bit PNG files by reducing the number of colors, and discard all unnecessary metadata.

Most PNG files have a compression rate of 50 to 70 percent, making them hard to distinguish even with the best vision. Use optimized images to reduce bandwidth and load time, the entire site using images by TinyJpg/TinyPng compression once, the results are no amount of code optimization can catch up.

TinyJpg/TinyPng development API

TinyJpg/TinyPng doesn’t open source its compression algorithm yet, but it does provide an API suitable for developers. Those interested can check out the development API documentation.

On the Node side, TinyJpg/TinyPng officially provides Tinify as the core JS library for compressed images. It’s easy to use, see the documentation. However, the development API is still not escape the charge, you want to pay a monthly or free, free to continue to see, local rich at will!

implementation

I also often use TinyJpg/TinyPng program ape, charge, that is impossible 😂. To find a breakthrough and solve problems is the most basic accomplishment of a programmer. We need to figure out what the problem is and what the problem is.

Analysis of the

From the above, you just need to transform the original TinyJpg/TinyPng function into the following function.

  • Automatic upload and download
  • compressiblejpgandpng
  • No quantity limit
  • There is a volume limit, the maximum volume cannot be exceeded5M
  • Details about compression success are displayed

Automatic processing

For the front-end developer, this mindless upload and download operation has to be automated to save trouble and effort. However, this operation should be handled in combination with Webpack. Whether it is developed as Loader or Plugin will be analyzed later. But careful students see the title will know what way to deal with.

The compression type

GIF compression is generally distorted, SVG is usually used for vector ICONS and rarely used for scene images, webP is rarely used due to compatibility issues, so JPG and PNG compression is enough. When filtering images, use the path module to check whether the file type is JPG or PNG. If yes, continue processing. Otherwise, no processing is performed.

Quantitative restrictions

Of course there is no quantity limit, if there are more than 20 images in the project, it is not batch processing, this can not be. For sites that can process some user files without having to log in, IP is often used to limit the number of operations a user can make. Some of you might say, why don’t you just refresh the page, compress 20 images each time, refresh and compress again, and if there are 500 images, refresh 25 times? That’s fun, isn’t it?

Since most Web architectures rarely service application servers directly, a layer of Nginx is typically set up as a proxy and load balancer, and some may even have multiple proxies. Since most Web architectures use Nginx as a reverse proxy, user requests are not sent directly to the application server. Instead, they are Forwarded to the server through the unified access layer configured by Nginx. Therefore, you can forge the IP by setting the HTTP request header field X-Forwarded-For.

X-forwarded-for Is an HTTP request header field that identifies the original IP address of the client connected to the Web server through a proxy or load balancer. Of course, the IP address is not fixed, and must be changed randomly for each request to fool the application server. If the application server adds forged IP identification, it may no longer be able to use random IP.

Volume restrictions

Volume limit this is understandable, there is no need to make such a large image, how much bandwidth and load time ah. When uploading an image, use the FS module to determine whether the file size exceeds 5M. If yes, the file will not be uploaded. Otherwise, the file will continue to be uploaded. Of course, to the TinyJpg/TinyPng interface judgment also ok.

Output information

Compression success or not to let others know, output the original size, compression size, compression rate and error messages, so that others know these processing information.

coding

With the above analysis, it’s time to start coding.

Randomly generate HTTP request headers

Since you can forge an IP address with X-Forwarded-For, you need a function that randomly generates an HTTP header field each time it requests an interface. Open tinyjpg.com or Tinypng.com and upload an image. Check the web/shrink interface in Chrome DevTools. Also, don’t focus on a single hostname for each request. It’s better to send it randomly to tinyjpg.com or tinypng.com. The request header information is generated randomly by encapsulating the RandomHeader function, and the HTTPS module is subsequently used to make the request with the configuration generated by RandomHeader() as an input parameter.

Trample is a Web/Node general-purpose utility library that includes general utility functions to help you write less general-purpose code. Please check the documentation for details and give a Star as encouragement.

const { RandomNum } = require("trample/node");

const TINYIMG_URL = [
    "tinyjpg.com"."tinypng.com"
];

function RandomHeader() {
    const ip = new Array(4).fill(0).map(() = > parseInt(Math.random() * 255)).join(".");
    const index = RandomNum(0.1);
    return {
        headers: {
            "Cache-Control": "no-cache"."Content-Type": "application/x-www-form-urlencoded"."Postman-Token": Date.now(),
            "User-Agent": "Mozilla / 5.0 (Windows NT 10.0; Win64; X64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"."X-Forwarded-For": ip
        },
        hostname: TINYIMG_URL[index],
        method: "POST".path: "/web/shrink".rejectUnauthorized: false
    };
}
Copy the code

Upload pictures and download pictures

Use Promise to encapsulate the functions for uploading and downloading images to facilitate subsequent Async/Await synchronization of asynchronous code. The following functions of the specific breakpoint debugging will not say, interested students debug the function of the input and output parameters ha!

const Https = require("https");
const Url = require("url");

function UploadImg(file) {
    const opts = RandomHeader();
    return new Promise((resolve, reject) = > {
        const req = Https.request(opts, res= > res.on("data".data= > {
            const obj = JSON.parse(data.toString());
            obj.error ? reject(obj.message) : resolve(obj);
        }));
        req.write(file, "binary");
        req.on("error".e= > reject(e));
        req.end();
    });
}

function DownloadImg(url) {
    const opts = new Url.URL(url);
    return new Promise((resolve, reject) = > {
        const req = Https.request(opts, res= > {
            let file = "";
            res.setEncoding("binary");
            res.on("data".chunk= > file += chunk);
            res.on("end".() = > resolve(file));
        });
        req.on("error".e= > reject(e));
        req.end();
    });
}
Copy the code

The compressed image

The compressed image information can be obtained by uploading the image function, and local files can be generated by downloading the image function according to the image information.

const Fs = require("fs");
const Path = require("path");
const Chalk = require("chalk");
const Figures = require("figures");
const { ByteSize, RoundNum } = require("trample/node");

async function CompressImg(path) {
    try {
        const file = Fs.readFileSync(path, "binary");
        const obj = await UploadImg(file);
        const data = await DownloadImg(obj.output.url);
        const oldSize = Chalk.redBright(ByteSize(obj.input.size));
        const newSize = Chalk.greenBright(ByteSize(obj.output.size));
        const ratio = Chalk.blueBright(RoundNum(1 - obj.output.ratio, 2.true));
        const dpath = Path.join("img", Path.basename(path));
        const msg = `${Figures.tick} Compressed [${Chalk.yellowBright(path)}] completed: Old Size ${oldSize}, New Size ${newSize}, Optimization Ratio ${ratio}`;
        Fs.writeFileSync(dpath, data, "binary");
        return Promise.resolve(msg);
    } catch (err) {
        const msg = `${Figures.cross} Compressed [${Chalk.yellowBright(path)}] failed: ${Chalk.redBright(err)}`;
        return Promise.resolve(msg); }}Copy the code

Compress target image

Once you have completed the corresponding functions in the above steps, you are free to compress the image, using an image as a demonstration.

const Ora = require("ora");

(async() = > {const spinner = Ora("Image is compressing......").start();
    const res = await CompressImg("src/pig.png");
    spinner.stop();
    console.log(res); }) ();Copy the code

You see, after the compression of stupid pigs have become handsome pigs, pigs can electric eyes are good pigs. See compresse-img for the source code.

To compress all qualified images in a specified folder, you can retrieve images via the FS module and map the individual image paths to CompressImg(path) using map(), followed by promise.all (). I’m not going to post the code here, but I’m going to do it on my own.

Package the function of compressed image as Loader or Plugin? We’ll do it step by step.

Loader&Plugin

Webpack is a front-end resource packaging tool that performs static analysis based on module dependencies and then generates corresponding static resources from these modules according to specified rules.

There are a lot of Webpack tutorials online, the author will not spend a large length of wordy, I believe that you are a standard Webpack configuration engineer. The following is a brief review of webpack composition, build mechanism and build process. It is believed that the role of Loader and Plugin in the WebPack build process can be determined from these knowledge points.

The WebPack mentioned in this article is based on WebPack V4Copy the code

composition

  • Entry: the entry
  • OutputOutput:
  • Loader: converter
  • Plugin: expander
  • ModeMode of:
  • Module: the module’s
  • TargetGoal:

Build mechanism

  • Transform the code through Babel and generate a single file dependency
  • Starting from the entry file, recursively analyze and generate the dependency graph
  • Package each reference module into an immediate function
  • Write the final bundle file tobundle.jsIn the

The build process

  • The initial
    • Initial parameters: Merges command line and configuration file parameters
  • compile
    • compiling: Based on parametersThe Compiler object, load allPlugin, the implementation ofrun()
    • Determine the entrance: Find all import files according to the configuration file
    • Compile the module: According to the entry file to find all dependent module relations, call allLoaderconvert
    • Generate the map: Get the converted content of each module and the dependencies between them
  • The output
    • Output resources: Assemble modules into blocks and then packages based on dependencies (Module → Chunk → Bundle)
    • Generate the file: Writes the confirmation output to the file system according to the configuration file
Loader

Loader is used to convert module source code, the author will translate it into converter. Loader converts all types of files into valid modules that WebPack can handle, and then reprocesses them using WebPack’s packaging capabilities.

Loader has the following features:

  • Single responsibility principle (Only one transformation is completed)
  • Convert received content
  • Return the conversion result
  • Support for chain calls

Loader converts all types of files into modules that can be directly referenced by the dependency graph of applications. Therefore, Loader can be used to compile some files, such as PUG → HTML, SASS → CSS, less → CSS, ES5 → ES6, TS → JS, etc.

Multiple Loaders can be used to process a file. The execution sequence of loaders is opposite to the configuration sequence, that is, the last Loader is executed first and the first Loader is executed last. The first Loader to execute receives the content of the source file as a parameter, the other Loaders receive the value returned by the previous Loader as a parameter, and the last Loader to execute returns the conversion result of the file. In a word: futukang assembly line factory workers.

The development ideas of Loader are summarized as follows:

  • throughmodule.exportsExport afunction
  • The first default argument to the function issource(Source file content)
  • Handling resources in function bodies (third-party module extensions can be introduced)
  • throughreturnReturns the final conversion result (as a string)
Loader should follow the principle of single responsibility. Each Loader does only one conversionCopy the code
Plugin

Plugin is used to extend a wider range of tasks, and I translate it as an extender. Plugins are so broad that they can be used as insertion points from start to finish in the Webpack build process, as long as you can’t think of anything you can’t do. Therefore, I believe that Plugin is more powerful than Loader.

Plugin has the following features:

  • Listening to thewebpackRun events that are broadcast during the lifecycle
  • Pass at the right timewebpackThe API provided alters the output
  • webpackThe Tapable event flow mechanism of the Plugin ensures orderality

A number of events are broadcast during the WebPack run life cycle, and the Plugin can listen for these events and change the output when appropriate through the WEBPack API. After Webpack started, execute new MyPlugin(OPTS) to initialize the custom Plugin to get the example in the process of reading configuration. After initialize Compiler object, Tap (PLUGIN_NAME, callback) listens for Webpack broadcast events, and when the specified events are caught, the associated business logic is manipulated through the Compilation object. In a word: make it your own.

The development ideas of Plugin are summarized as follows:

  • throughmodule.exportsExport aA function or class
  • inFunction prototype or classThe bindingapply()accessThe Compiler object
  • inapply()Specifies a binding towebpackIts own event hook
  • Pass in the event hookwebpackAPI processing resources provided (third-party module extensions can be introduced)
  • throughwebpackThe provided method returns the resource
The Compiler and Compilation passed to each Plugin are the same reference, and modifying their properties will affect subsequent plugins, so be carefulCopy the code
Loader/Plugin
  • nature
    • LoaderEssentially a function, the transform receives the content and returns the result of the transform
    • PluginIt’s essentially a class that listenswebpackRun events that are broadcast in the lifecycle and pass when appropriatewebpackThe API provided alters the output
  • configuration
    • Loaderinmodule.rule, the type is array, each item corresponds to a module resolution rule
    • PlugininpluginThe type is array, each item corresponds to an extender instance, and the parameters are passed in through the constructor

encapsulation

Analysis of the

Loader and Plugin have many differences in role positioning and execution mechanism. How to choose? Each has its own good, of course, or need to choose after analysis.

Loader plays the role of converter in Webpack, used to convert module source code, simple understanding is to convert files into another form of file, and the topic of this paper is compressed image, JPG or JPG after compression, PNG or PNG after compression, in terms of file type is still unchanged. The Loader conversion process is attached to the entire Webpack build process, meaning that the packaging time includes the time cost of compressing images, which is a bit of a violation of principle for the pursuit of Webpack performance optimization. Plugin just listens for events broadcast during the webPack run life cycle and changes the output results through the API provided by WebPack when appropriate, so you can insert compressed image operations after the entire WebPack build process is complete (after all the packaged file output is completed). In other words, the packaging time no longer includes the time cost of compressing the image.

Therefore, Plugin is preferred depending on the requirements.

coding

With the Plugin development thought above, it’s time to start coding.

I named this Plugin as tinyimg-webpack-plugin. Tinyimg means the combination of TinyJpg and TinyPng.

Create a new project with the following directory structure.

│ ├─ SRC │ ├─ index.js │ ├─ util │ ├─ Getting.js │ ├─ Setting │ ├─.gitignore │ ├─ SRC │ ├─ index .nPMIgnore ├─ License ├─ Package. json ├─ Readme.mdCopy the code

The main documents are as follows.

  • src
    • Index.js: entry function
    • Schema. json: verifies parameters
  • util
    • Geting.js: a collection of constants
    • Setting. js: set of functions

Install the modules required by the project, which are consistent with the above compresse-img dependencies. Install schema-utils to verify whether Plugin parameters meet the requirements.

npm i chalk figures ora schema-utils trample
Copy the code

Encapsulates a constant collection and a function collection

Wrap the above TINYIMG_URL and RandomHeader() from compressed-img into the tool set, where constants IMG_REGEXP and PLUGIN_NAME are added to the constant set.

// getting.js
const IMG_REGEXP = /\.(jpe? g|png)$/;

const PLUGIN_NAME = "tinyimg-webpack-plugin";

const TINYIMG_URL = [
    "tinyjpg.com"."tinypng.com"
];

module.exports = {
    IMG_REGEXP,
    PLUGIN_NAME,
    TINYIMG_URL
};
Copy the code
// setting.js
const { RandomNum } = require("trample/node");

const { TINYIMG_URL } = require("./getting");

function RandomHeader() {
    const ip = new Array(4).fill(0).map(() = > parseInt(Math.random() * 255)).join(".");
    const index = RandomNum(0.1);
    return {
        headers: {
            "Cache-Control": "no-cache"."Content-Type": "application/x-www-form-urlencoded"."Postman-Token": Date.now(),
            "User-Agent": "Mozilla / 5.0 (Windows NT 10.0; Win64; X64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"."X-Forwarded-For": ip
        },
        hostname: TINYIMG_URL[index],
        method: "POST".path: "/web/shrink".rejectUnauthorized: false
    };
}

module.exports = {
    RandomHeader
};
Copy the code

Exports a function or class through module.exports

// index.js
module.exports = class TinyimgWebpackPlugin {};
Copy the code

Bind Apply () to the function prototype or class to access the Compiler object

// index.js
module.exports = class TinyimgWebpackPlugin {
    apply(compiler) {
        // Do Something}};Copy the code

Specify an event hook in Apply () that is bound to webPack itself

As you can see from the above analysis, the compressed image operation should be inserted after all the output of the packaged file, so the corresponding event hook should be selected at this time. As you can see from the Webpack Compiler Hooks API documentation, emit is exactly the event hook required for this Plugin. Emit is executed before generating the resource to the output directory, at which point the data and output paths of all image files can be retrieved.

The configuration is set to make it easier to enable features and print logs under certain conditions.

  • Enabled: Whether to enable the function
  • Logged: Indicates whether logs are logged

When the relevant business logic is processed in apply(), it is possible to use Plugin inputs, and then the parameters need to be validated. Define a Schema for Plugin, and verify Plugin inputs with schema-utils.

// schema.json
{
    "type": "object"."properties": {
        "enabled": {
            "description": "start plugin"."type": "boolean"
        },
        "logged": {
            "description": "print log"."type": "boolean"}},"additionalProperties": false
}
Copy the code
// index.js
const SchemaUtils = require("schema-utils");

const { PLUGIN_NAME } = require(".. /util/getting");
const Schema = require("./schema");

module.exports = class TinyimgWebpackPlugin {
    constructor(opts) {
        this.opts = opts;
    }
    apply(compiler) {
        const { enabled } = this.opts;
        SchemaUtils(Schema, this.opts, { name: PLUGIN_NAME });
        enabled && compiler.hooks.emit.tap(PLUGIN_NAME, compilation= > {
            // Do Something}); }};Copy the code

Integrate compresse-img into Plugin

There will be some minor modifications during the integration process, so you can compare and see what details have changed.

// index.js
const Fs = require("fs");
const Https = require("https");
const Url = require("url");
const Chalk = require("chalk");
const Figures = require("figures");
const { ByteSize, RoundNum } = require("trample/node");

const { RandomHeader } = require(".. /util/setting");

module.exports = class TinyimgWebpackPlugin {
    constructor(opts){... }apply(compiler){... }async compressImg(assets, path) {
        try {
            const file = assets[path].source();
            const obj = await this.uploadImg(file);
            const data = await this.downloadImg(obj.output.url);
            const oldSize = Chalk.redBright(ByteSize(obj.input.size));
            const newSize = Chalk.greenBright(ByteSize(obj.output.size));
            const ratio = Chalk.blueBright(RoundNum(1 - obj.output.ratio, 2.true));
            const dpath = assets[path].existsAt;
            const msg = `${Figures.tick} Compressed [${Chalk.yellowBright(path)}] completed: Old Size ${oldSize}, New Size ${newSize}, Optimization Ratio ${ratio}`;
            Fs.writeFileSync(dpath, data, "binary");
            return Promise.resolve(msg);
        } catch (err) {
            const msg = `${Figures.cross} Compressed [${Chalk.yellowBright(path)}] failed: ${Chalk.redBright(err)}`;
            return Promise.resolve(msg); }}downloadImg(url) {
        const opts = new Url.URL(url);
        return new Promise((resolve, reject) = > {
            const req = Https.request(opts, res= > {
                let file = "";
                res.setEncoding("binary");
                res.on("data".chunk= > file += chunk);
                res.on("end".() = > resolve(file));
            });
            req.on("error".e= > reject(e));
            req.end();
        });
    }
    uploadImg(file) {
        const opts = RandomHeader();
        return new Promise((resolve, reject) = > {
            const req = Https.request(opts, res= > res.on("data".data= > {
                const obj = JSON.parse(data.toString());
                obj.error ? reject(obj.message) : resolve(obj);
            }));
            req.write(file, "binary");
            req.on("error".e= >reject(e)); req.end(); }); }};Copy the code

Resources are handled in event hooks through the API provided by WebPack

The compilation. Assets was used to obtain all the objects of the packaged files, and JPG and PNG were screened out. Map () was used to map the single image data to this.pressimg (file), and promise.all () was used.

The whole business logic is a combination of Promise and Async/Await, two common ES6 features, which together are extremely interesting to play with asynchronous programming. For more details on both of them, check out my 4,000 likes and 140,000 views article, “All ES6 features in 15,000 words.”

// index.js
const Ora = require("ora");
const SchemaUtils = require("schema-utils");

const { IMG_REGEXP, PLUGIN_NAME } = require(".. /util/getting");
const Schema = require("./schema");

module.exports = class TinyimgWebpackPlugin {
    constructor(opts){... }apply(compiler) {
        const { enabled, logged } = this.opts;
        SchemaUtils(Schema, this.opts, { name: PLUGIN_NAME });
        enabled && compiler.hooks.emit.tap(PLUGIN_NAME, compilation= > {
            const imgs = Object.keys(compilation.assets).filter(v= > IMG_REGEXP.test(v));
            if(! imgs.length)return Promise.resolve();
            const promises = imgs.map(v= > this.compressImg(compilation.assets, v));
            const spinner = Ora("Image is compressing......").start();
            return Promise.all(promises).then(res= > {
                spinner.stop();
                logged && res.forEach(v= > console.log(v));
            });
        });
    }
    async compressImg(assets, path){... }downloadImg(url){... }uploadImg(file){... }};Copy the code

This resource is returned via a method provided by Webpack

Since the compression of the image is done after the entire Webpack build process is complete, there is nothing to return, so it is not processed.

Control the WebPack dependency version

Since tinyimg-webpack-plugin is based on webpack V4, add peerDependencies to package.json to tell the module installing the plugin that it must have the peerDependencies in it.

{
    "peerDependencies": {
        "webpack": "> = 4.0.0"."webpack-cli": "> = 3.0.0"}}Copy the code

conclusion

According to the development ideas summarized above step by step to complete the coding, in fact, is quite simple. If you need to develop some plugins related to your project, you need to familiarize yourself with the Webpack Compiler Hooks API documentation.

Tinyimg-webpack-plugin source code please check here, Star a how, xi xi.

test

The whole Plugin development completed, the next need to go through a test process, to see if the compression of the picture of the extender run. I believe that you are a standard Webpack configuration engineer and can write your own test Demo to verify your Plugin.

Create the test folder in the root directory and add the files according to the following directory structure.

Tinyimg webpack - plugin ├ ─ test │ ├ ─ SRC │ │ ├ ─ img │ │ │ ├ ─ the favicon. Ico │ │ │ ├ ─ gz. JPG │ │ │ ├ ─ pig - 1. JPG │ │ │ ├ ─ Pig - 2. JPG │ │ │ ├ ─ pig - 3. JPG │ │ ├ ─ index. The HTML │ │ ├ ─ index. The js │ │ ├ ─ index. The SCSS │ │ ├ ─ reset. CSS │ └ ─ webpack. Config. JsCopy the code

Install webPack-related configuration modules required for test Demo.

npm i -D @babel/core @babel/preset-env babel-loader clean-webpack-plugin css-loader file-loader html-webpack-plugin mini-css-extract-plugin node-sass sass sass-loader style-loader url-loader webpack webpack-cli webpackbar
Copy the code

After installing webpack.config.js, start to improve the code, the code is a bit too much, directly paste the link, please stamp here.

Finally, insert the following NPM scripts in package.json and run the NPM run test debug test Demo.

{
    "scripts": {
        "test": "webpack --config test/webpack.config.js"}}Copy the code

release

Publishing to the NPM repository is as simple as a few lines of command. If you haven’t already, go to NPM and set up an account. If the current mirror is taobao mirror, perform NPM config set registry https://registry.npmjs.org/ switch back to the source image.

The next wave of operations completes the publication.

  • Enter the directory:cd my-plugin
  • Login Account:npm login
  • Check status:npm whoami
  • Release module:npm publish
  • Exit account:npm logout

If you don’t want to remember so many commands, you can use the author developed pkG-master one-key release, if there are some errors will immediately interrupt the release and prompt error messages, is a very good integration of the creation and release of NPM module management tools. Please check the documentation for details and give a Star as encouragement.

The installation

npm i -g pkg-master

use

The command abbreviations function describe
pkg-master create pkg-master c Create a module Module-generatingBased on the file
pkg-master publish pkg-master p Publish module Detection of NPMRuntime environmentandAccount status, the module is automatically published

Access to the

The installation

npm i tinyimg-webpack-plugin

use

configuration function format describe
enabled Enable or not true/false You are advised to enable this function only in the production environment
logged Whether to print logs true/false Print processing information

Insert the following code in the webpack.config.js or webpack configuration.

Used in CommonJS

const TinyimgPlugin = require("tinyimg-webpack-plugin");

module.exports = {
    plugins: [
        new TinyimgPlugin({
            enabled: process.env.NODE_ENV === "production".logged: true}})];Copy the code

Used in the ESM

Must be used in a Node environment supported by Babel

import TinyimgPlugin from "tinyimg-webpack-plugin";

export default {
    plugins: [
        new TinyimgPlugin({
            enabled: process.env.NODE_ENV === "production".logged: true}})];Copy the code

A zero-configuration, out-of-the-box React/Vue application is recommended for automated build scaffolding

Bruce-cli is an automated build scaffold for React/Vue applications. It has zero configuration out of the box and is ideal for beginners, beginners, and quick development projects. It can also override its default configuration by creating brucerc.js files. Make the project structure simpler. Remember to check the documentation when using, and give a Star if you like.

Of course, the author has integrated the Tinyimg-Webpack-plugin into the Bruce-CLI, with zero configuration right out of the box.

  • Github address: Please click here
  • Official documentation: Please click here
  • Gold digger documentation: Please poke here

conclusion

In general, developing a Webpack Plugin is not difficult. You just need to analyze the requirements, understand the events broadcast during the Webpack runtime life cycle, and write a custom Plugin to change the output through the API provided by Webpack when appropriate.

If you find tinyimg-webpack-plugin helpful, please put forward your valuable suggestions on the Issue. I will read and integrate your suggestions carefully. If you like the Tinyimg-webpack-plugin, please give a Star, or Fork this project to your Github and customize the functionality according to your needs.

conclusion

❤️ follow + like + favorites + comments + forward ❤️, original is not easy, encourage the author to create more high-quality articles

Follow public accountIQ front-end, a focus on CSS/JS development skills of the front-end public number, more front-end small dry goods waiting for you oh

  • Reply after attentiondataFree access to study materials
  • Reply after attentionInto the group ofGet you into the tech community
  • Welcome to attentionIQ front-endAnd moreCSS/JS development skillsOnly in the public account push