preface

In our normal development, we will often use Webpack, but most of the time we do not do any configuration (Webpack4) can also be developed, but the default configuration is really enough for us to use? So this article will take you through the doors of WebPack.

Preknowledge

Before we learn webpack, let’s learn the necessary auxiliary concepts

Path to knowledge

Before we do that, it’s important to understand the NodeJS path knowledge that webPack will use :NodeJS path knowledge

Entry of core concepts

Entry specifies the webpack Entry

The use of the Entry

Single entry: Entry is a string

module.exports = {
    entry: './src/index.js'
};
Copy the code

Multiple entry: Entry is an object

module.exports = {
    entry: {
        index: './src/index.js'.manager: './src/manager.js'}};Copy the code

Output of core concepts

Output tells WebPack how to Output the compiled file to the specified path on disk

Usage of Output: single entry configuration

module.exports = {
    entry: './src/index.js'.output: {
        filename: 'bundle.js', path: __dirname + '/dist'}};Copy the code

Usage of Output: multi-entry configuration

module.exports = {
    entry: {
        app: './src/app.js'.search: './src/search.js'
    },
    output: {
        filename: '[name].js'.path: __dirname + '/dist'}};Copy the code

Ensure that the file name is unique with the [name] placeholder

Core concepts Loader

Is itself a function that takes the source file as an argument and returns the result of the transformation.

What are the common Loaders?

Babel-loader, convert ES6 + syntax to ES3 / ES5 syntax CSS-loader, style-loader, SCSS-loader, less-loader, postCSs-loader, file-loader, ... class = 'class2' > ts-loader, convert TS to JS raw-loader, import files as strings...Copy the code

The use of the Loader

const path = require('path');
module.exports = {
    output: {
        filename: 'bundle.js'
    },
    module: {
        rules: [{test: /\.js$/, use: 'babel-loader'}}};Copy the code

Test Specifies the matching rule

Use Specifies the loader name

Core concepts Plugins

Plug-ins are used for bundle optimization, resource management, and environment variable injection throughout the build process

Common Plugins

MiniCssExtractPlugin extracts CSS from the bundle file as a separate CSS file. HtmlWebpackPlugin creates HTML to carry the output bundle file UglifyWebpackPlgin compression JS removalconsoleSuch as specified statement...Copy the code

The use of the Plugins

const path = require('path');
module.exports = {
    output: {
        filename: 'bundle.js'
    },
    plugins: [
        new HtmlWebpackPlugin({template:'./src/index.html'}})];Copy the code

All plug-ins should be placed in the plugins array

Practical article

To do this, we need to install the following dependencies:

npm install webpack webpack-cli -D
Copy the code

Convert ES6 to ES5 syntax

The installation depends on NPM i@babel/core@babel /preset-env babel-loader -d

Webpack.config.js is configured as follows:

module: {
    rules: [{test: /\.js$/,
            use: 'babel-loader'}]};Copy the code

Add ES6 Babel Preset configuration

The configuration file for Babel is:.babelrc

{
    "presets": [
        "@babel/preset-env" // ES2015+ code can be automatically converted to ES5 based on the target browser or runtime environment configured.]}Copy the code

Parsing the React JSX

Installation required dependencies

npm i @babel/preset-react -D

The configuration is as follows:

The configuration file for Babel is:.babelrc

{
    "presets": [
        "@babel/preset-env"."@babel/preset-react"]."plugins": []}Copy the code

Resource parsing: Parses the CSS

npm install style-loader css-loader less-loader -D
Copy the code
module: {
        rules: [{test: /\.css$/,
                use: [
                    'style-loader'.'css-loader'] {},test: /\.less$/,
                use: [
                    'style-loader'.'css-loader'.'less-loader']},]},Copy the code

Css-loader is used to load.css files and convert them to CommonJS objects

Style-loader inserts the style into the head via the

Style – loader configuration:

options: {
    insertAt: 'top'.// Insert styles into 
    singleton: true.// Merge all the style tags into one
}
Copy the code

Less-loader converts less syntax to CSS syntax

Note: Loaders are executed from bottom to top and from right to left

Resource parsing: Parsing image and font files

npm install url-loader file-loader -D
Copy the code
module: {
        rules: [{test: /\.(png|jpg|gif|jpeg)$/,
                use: [
                    {
                        loader: 'url-loader'.options: {
                            limit: 10240}}]}, {test: /\.(woff|woff2|eot|ttf|otf)$/,
                use: 'file-loader'}},Copy the code

File-loader is used to process font files

Url-loader can be used to process images and fonts and limit can be set to automatically base64 for smaller resources

File-loader is used in url-loader

File listening in Webpack

File monitoring is the automatic reconstruction of a new output file when changes are detected in the source code.

Webpack enables listening mode in two ways:

· Start webpack command with –watch parameter [recommended]

· Set Watch: true in configuring webpack.config.js

File listening use in Webpack

Configure in package.json

{
    "name": "hello-webpack",
    "main": "index.js",
    "scripts": {
        "watch": "webpack --watch",
        "build": "webpack --config webpack.prod.js",
    },
}
Copy the code

Hot update: webpack-dev-server

npm install webpack-dev-server -D
Copy the code

Package. json:

{
    "scripts": {
        "dev": "webpack-dev-server --open --mode development"}},Copy the code

— Open: automatically opens the browser

Webpack-dev-server does not refresh the browser and does not output files, but instead puts them in memory

Using HotModuleReplacementPlugin plugin (webpack plug-ins) can make the browser automatically refresh

Webpack.config.js:

const webpack = require('webpack');
plugins: [
    new webpack.HotModuleReplacementPlugin(),
],
devServer: {hot: true.contentBase: [path.join(__dirname, "public"), path.join(__dirname, "assets")].//contentBase is used to configure the directory that provides the content of additional static files
    proxy: {
        '/api': {
            target: "http://localhost:3000".// Proxies requests with/API in the URL to the local service on port 3000
            pathRewrite: { '^/api': ' ' }, // Remove the API from the path part of the URL}},before(app) {
            app.get('/some/path'.function(req, res) { // Returns custom JSON data when accessing the /some/path path
                res.json({ custom: 'response'}}})})Copy the code

Before can be used to intercept partial requests to return specific content, or to implement simple data mocks, before being processed by the static resource middleware of Webpack-Dev-server.

After is used less often than webpack-dev-server static resource middleware and can be used to print logs or do extra processing.

Principle analysis of thermal renewal

Webpack Compile: Compile JS into bundles

HMR Server: outputs hot updated files to HMR Rumtime

Bundle server: provides browser access to files (e.g. Localhost :8080/ bundle.js)

HMR Rumtime: During the packaging phase of the development phase, it is injected into the bundle.js on the browser side. The bundle.js on the browser side establishes a connection with the browser, usually a Websocket, so that the file changes can be updated

Bundle.js: Build the output file

File hash value

The file hash is the suffix of the output file name

How are file hashes generated

Hash: Relates to the construction of the entire project. The Hash value for the entire project will change whenever the project file is modified

Chunkhash: Related to chunks packed by Webpack, different entries will generate different Chunkhash values

Contenthash: Defines hash based on the content of the file. Contenthash does not change if the content of the file remains unchanged

JS file hash Settings

To set filename for output, use [chunkhash]

output: {
    filename: '[name][chunkhash:8].js'.path: __dirname + '/dist'
}
Copy the code

Note: Chunkhash cannot be used with hot updates

CSS file hashing Settings

Set filename of MiniCssExtractPlugin,

The use of [contenthash]

npm install mini-css-extract-plugin -D
Copy the code
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
plugins: [
     new MiniCssExtractPlugin({
         filename: `[name][contenthash:8].css`
     });
]
Copy the code

If we want to extract CSS, then style-loader cannot be used because the two are mutually exclusive, so we can write:

module: {
        rules: [{test: /\.css$/,
                use: [
                    - 'style-loader',
                    + MiniCssExtractPlugin.loader
                    'css-loader'] {},test: /\.less$/,
                use: [
                    - 'style-loader',
                    + MiniCssExtractPlugin.loader
                    'css-loader'.'less-loader']},]},Copy the code

Image & font file hashing Settings

module: {
rules: [{test: /\.(png|svg|jpg|gif)$/,
        use: [{
              loader: 'file-loader ', options: {name: 'img/[name][hash:8].[ext] '}}]}]Copy the code

Placeholder introduction

[ext] Suffix of the resource name

[name] Indicates the file name

[path] Relative path of the file

[Folder] Indicates the folder where the file resides

[contenthash] The hash file content is generated by md5 by default

[hash] The hash of the file is generated by MD5 by default

Code compression

HTML compression

CSS compression

JS compressed

JS file compression

npm install uglifyjs-webpack-plugin -D

const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

plugins: [
     new UglifyJsPlugin()
]
Copy the code

Compression of CSS files

npm install optimize-css-assets-webpack-plugin cssnano -D
Copy the code

Optimize – CSS-assets-webpack-plugin with CSsnano

const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
plugins: [
     new OptimizeCSSAssetsPlugin({
         assetNameRegExp: /\.css$/g,
         cssProcessor: require('cssnano')})]Copy the code

HTML file compression

npm install html-webpack-plugin -D
Copy the code

Use htMl-webpack-plugin to set compression parameters

Webpack. Config. Js

const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
     new HtmlWebpackPlugin({
         template: path.join(__dirname, '/ dist/index. HTML), filename:'Index.html, 'chunks: ['index '], inject: true, collapseWhitespace: true, // dissolve preserveLineBreaks: MinifyCSS: true, minifyJS: true, removeComments: false}})]Copy the code

Automatically clean up the build directory

Avoid manually deleting dist each time before a build

Use the clean – webpack – the plugin

The output directory specified by output is deleted by default

npm install clean-webpack-plugin -D

const CleanWebpackPlugin = require('clean-webpack-plugin');
plugins: [
    new CleanWebpackPlugin(),
]
Copy the code

PostCSS plugin Autoprefixer adds the CSS3 prefix

IE: Trident(-MS) Firefox: Geko(-moz) Google: Webkit(-webKit) Openg: Presto(-O)

npm install postcss-loader autoprefixer -D

Copy the code
 module: {
        rules: [{test: /\.less$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader'.'less-loader',
                    {
                        loader: 'postcss-loader'.options: {
                            plugins: () = > [
                                require('autoprefixer') ({browsers: ['last 2 version'.'> 1%'.'ios 7']})]}},]},]},Copy the code

Mobile CSS PX automatically converts to REM

What is REM?

W3C definition of REM: font-size of the root element

Rem vs. PX:

Rem is the relative unit

Px is the absolute unit

Use px2rem – loader

Calculate font size for the root element when rendering the page, using the Lib-Flexible library of Hand Cleaning

npm install px2rem-loader -D
npm i lib-flexible raw-loader@0.51. -S

module: {
        rules: [{test: /\.less$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader'.'less-loader',
                    {
                        loader: 'postcss-loader'.options: {
                            plugins: () = > [
                                require('autoprefixer') ({browsers: ['last 2 version'.'> 1%'.'ios 7']})]}}, {loader: 'px2rem-loader'.options: {
                            remUnit: 75.remPrecision: 8 // Convert rem decimal places}}]}Copy the code

Use raw-loader to inline lib-flexible in index. HTML

<script>The ${require('raw-loader! babel-loader! . /node_modules/lib-flexible')}
</script>
Copy the code

Raw – loader inline HTML

<script>The ${require(' raw-loader! babel-loader! . /meta.html')}
</script>
Copy the code

Multi-page application (MPA) concept

Each time the page jumps, the background server will return a new HTML document, this type of site is also called multi-page site, also known as multi-page application.

Multi-page packaging universal solution: dynamically get entry and set htML-webpack-plugin

Using the glob library

Write the following code in webpack.config.js:

npm install glob -D

const glob = require('glob');
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');
const setMPA = () = > {
    const entry = {};
    const htmlWebpackPlugins = [];
    const entryFiles = glob.sync(path.join(__dirname, './src/*/index.js'));

    Object.keys(entryFiles)
        .map((index) = > {
            const entryFile = entryFiles[index];

            const match = entryFile.match(/src\/(.*)\/index\.js/);
            const pageName = match && match[1];

            entry[pageName] = entryFile;
            htmlWebpackPlugins.push(
                new HtmlWebpackPlugin({
                    inlineSource: '.css$'.template: path.join(__dirname, `src/${pageName}/index.html`),
                    filename: `${pageName}.html`.chunks: ['vendors', pageName],
                    inject: true.minify: {
                        html5: true.collapseWhitespace: true.preserveLineBreaks: false.minifyCSS: true.minifyJS: true.removeComments: false}})); });return {
        entry,
        htmlWebpackPlugins
    }
}
const { entry, htmlWebpackPlugins } = setMPA();

module.exports = {
    entry: entry,
    plugins: [].concat(htmlWebpackPlugins)
}
Copy the code

DefinePlugin

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      PRODUCTION: JSON.stringify(true), // const PRODUCTION = true
      VERSION: JSON.stringify('5fa3b9'), // const VERSION = '5fa3b9'
      BROWSER_SUPPORTS_HTML5: true.// const BROWSER_SUPPORTS_HTML5 = 'true'
      TWO: '1 + 1'.// const TWO = 1 + 1,
      CONSTANTS: {
        APP_VERSION: JSON.stringify('1.1.2') // const CONSTANTS = {APP_VERSION: '1.1.2'}}})],}Copy the code

With the above configuration, you can access the configured variables in the application code file, such as:

console.log("Running App version " + VERSION);

if(! BROWSER_SUPPORTS_HTML5)require("html5shiv");
Copy the code

If the configured value is a string, the entire string is executed as a code snippet, with the result as the final variable value, “1+1” above, resulting in 2

If the configured value is not a string and is not an object literal, then the value is converted to a string, such as true, resulting in ‘true’.

If an object literal is configured, all keys of that object are defined in the same way

So we can understand why json.stringify () is used, since json.stringify (true) results in ‘true’ and json.stringify (“5fa3b9”) results in ‘5fa3b9’.

copy-webpack-plugin

const CopyWebpackPlugin = require('copy-webpack-plugin')

module.exports = {
  // ...
  plugins: [
    new CopyWebpackPlugin([
      { from: 'src/file.txt'.to: 'build/file.txt',},// As the name implies, from configures the source, to configures the destination path
      { from: 'src/*.ico'.to: 'build/*.ico' }, // Configuration items can use glob
      // Many replication rules can be configured])],}Copy the code

ProvidePlugin

Can be understood as more convenient to introduce, such as jquery, LoDash

new webpack.ProvidePlugin({
     _: 'lodash'.//import _ from lodash
     $: 'jquery'//import $ from jquery
})
Copy the code

IgnorePlugin

This can be obtained directly using webpack.ignoreplugin.

This plugin is used to ignore specific modules so that WebPack does not pack them. For example, if we use moment.js, there will be a lot of I18N code in it, which will result in a large package file, but the actual scene does not need this I18N code file, we can use IgnorePlugin to ignore this code file, configuration as follows:

module.exports = {
  // ...
  plugins: [
    new webpack.IgnorePlugin(/^\.\/locale$/./moment$/)]}Copy the code

The IgnorePlugin configuration takes two parameters. The first is a regular expression that matches the path of the incoming module, and the second is the name of the directory in which the module resides.

SourceMap type

devtool:

source-map:

Development: being – the module – the eval – source – the map

Production: hidden – source – the map

Base library separation

React, React – DOM, Axios, and Element-UI base packages are added to the bundle via CDN

NPM install html-webpack-externals-plugin -d Html-webPackexternals-plugin is usedconst HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin');
plugins:[
    new HtmlWebpackExternalsPlugin({
                externals: [{module: 'react'.entry: 'cdn.com/react.min.js'.global: 'React'}, {module: 'axios'.entry: 'cdn.com/axios.min.js'.global: 'Axios',},]}),]Copy the code

SplitChunksPlugin is used for common script separation

SplitChunksPlugin is a built-in Webpack4 plugin that replaces CommonsChunkPlugin of WebPack3

Async Libraries introduced asynchronously are detached (default)

Initial synchronizes the imported libraries for separation

All All imported libraries are separated (recommended)

optimization: {
     splitChunks: {
        chunks: 'all'.minSize: 30000.// The smallest size of the public package to pull out
        maxSize: 0.// The maximum size of the public package to be removed
        minChunks: 1.// The number of times a piece of code is used in more than one place
        maxAsyncRequests: 5.maxInitialRequests: 3.// The number of asynchronous JS requests made by the browser simultaneously
        name: true.cacheGroups: {
            vendors: {
                test: /(axios|react)/,
                priority: -10.minChunks: 1}}}}Copy the code

TreeShaking (treeShaking optimization)

Concept: A module may have multiple methods, and as long as one of them is used, the whole file will be sent to the bundle. Tree shaking is simply putting used methods into the bundle, while unused methods are erased during the Uglify phase.

Set modules: false to webpack4 by default. Babelrc must be ES6 syntax, CommonJS syntax is not supported.

Production mode is enabled by default

TreeShaking:

The result of code execution is not used the result of code execution is not executed, unreachable code only affects dead variablesCopy the code

The Tree – shaking principle

Using the features of ES6 module:

Can only appear as a statement at the top level of a module

The module name for import must be a string constant

Import binding is immutable

Code erasure: The Uglify phase removes useless code

Tree-shaking fails if there are side effects in the code

You can configure sideEffect:[] in package.json, such as babel-polyfill

The built code has a lot of closure code

A lot of scoped code wrapped around, resulting in an increase in size (the more modules, the more noticeable)

The functions created while running the code have more scope and more memory overhead

Module transformation analysis

Conclusion:

Modules transformed by WebPack are wrapped in a layer

Import will be converted to __webpack_require

Further analyze the module mechanism of Webpack

Analysis: Packaged is an IIFE (anonymous closure)

Modules is an array, and each item is a module initialization function

__webpack_require is used to load modules and returns module.exports

Use scope reactive to eliminate a large number of closures

How it works: Put all module code in a function scope in reference order, and then rename some variables appropriately to prevent variable name conflicts

Must be ES6 syntax, CJS does not support

plugins: [
 new webpack.optimize.ModuleConcatenationPlugin()
]
Copy the code

Lazy loading of modules

One feature of WebPack is to break up your code base into chunks and load them when the code runs and needs them.

CommonJS: the require. Ensure

ES6: Dynamic import (no native support yet, Babel conversion required)

How to use dynamic import?

Install Babel plugin ES6: dynamic import (no native support yet, Babel conversion required)

Configure in. Babelrc:

npm install @babel/plugin-syntax-dynamic-import --save-dev

{
    "plugins": ["@babel/plugin-syntax-dynamic-import"],
}
Copy the code

Use in code:

loadComponent() {
    import('./text.js').then((Text) = > {
        this.setState({
            Text: Text.default
        });
    });
}
Copy the code

This way text.js is automatically split into a separate file when packaged and loaded when we call this method, which is an optimization

Webpack packages libraries and components

Webpack can be used to package applications as well as JS libraries

Implement a plus library package

You need to package the compressed and uncompressed versions

Support AMD/CJS/ESM module introduction

/ / index under the SRC. Js
export default function add(a, b) {
    return a + b;
}
Copy the code

How do I expose the library?

.min compression only can be matched using the TerserPlugin plugin

npm install terser-webpack-plugin -D
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
    entry: {
        'large-number': './src/index.js'.'large-number.min': './src/index.js'
    },
    output: {
        filename: '[name].js'.library: 'largeNumber'.libraryTarget: 'umd'.libraryExport: 'default'
    },
    mode: 'none'.optimization: {
        minimize: true.minimizer: [
            new TerserPlugin({
                include: /\.min\.js$/,})]}}/ / index. In js
if (process.env.NODE_ENV === 'production') {
    module.exports = require('./dist/large-number.min.js');
} else {
    module.exports = require('./dist/large-number.js');
}
Copy the code

Set entry file

The main field of package.json is index.js

Then webpack is ready

SSR

Ideas:

The Server uses the React – DOM/Server renderToString method to render the React component as a string. The server route returns the corresponding template

The client

Package out components for the server

<! --search.html-->
<body>
    <div id="root"><! --HTML_PLACEHOLDER--></div>
</body>
Copy the code

Annotations are used here for placeholders

const fs = require('fs');
const path = require('path');
const express = require('express');
const { renderToString } = require('react-dom/server');
const SSR = require('.. /dist/search-server');
const template = fs.readFileSync(path.join(__dirname, 'search.html'), 'utf-8');

const server = (port) = > {
    const app = express();

    app.use(express.static('dist'));
    app.get('/search'.(req, res) = > {
        const html = renderMarkup(renderToString(SSR));
        res.status(200).send(html);
    });

    app.listen(port, () = > {
        console.log('Server is running on port:' + port);
    });
};

server(process.env.PORT || 3000);

const renderMarkup = (str) = > {
    return template.replace('<! --HTML_PLACEHOLDER-->', str)
}
Copy the code

SSR optimization

Reduce the amount of data required for the first screen and eliminate redundant data and requests.

Good cache control, reasonable cache of data/pages;

Requests for pages are delivered as streams;

How do I optimize the command line build log

Use the friendly – errors – webpack – the plugin

const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
plugins: [
    new FriendlyErrorsWebpackPlugin()
],
stats: 'errors-only'
Copy the code

Build error reporting

plugins:[
    new FriendlyErrorsWebpackPlugin(),
    function() {
            this.hooks.done.tap('done'.(stats) = > {
                if (stats.compilation.errors && stats.compilation.errors.length && process.argv.indexOf('--watch') = = -1)
                {
                    console.log('Error reporting');
                    process.exit(1); }})}]Copy the code

Compiler triggers the done hook at the end of each build

Tap Is this.plugin. plugin in webpack3

The process.exit specification in Node.js

0 indicates successful completion. In the callback function, err is null

Err is not null in the callback function. Err. code is the number passed to exit

Published to the NPM

Upgrade patch version: NPM Version Patch// A bug is fixedUpgrade minor version: NPM Version Minor// Publish fetureMajor version: NPM Version Major// This is usually a major update
Copy the code

Webpack optimization article

Webpack-bundle-analyzer analyzes volumes

npm install webpack-bundle-analyzer -D
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
plugins:[
    new BundleAnalyzerPlugin(),
]
Copy the code

The size will be displayed on port 8888 when the build is complete

It is generally recommended to use older versions of Node and WebPack because they are optimized internally

The reason for using WebPack4

V8 optimizations (for of instead of forEach, Map and Set instead of Object, includes instead of indexOf)

The default is the faster MD4 hash algorithm

Webpack AST can be passed directly from the Loader to the AST, reducing parsing time

Use string methods instead of regular expressions

Multi-process/multi-instance build: Alternative to resource parallel parsing

npm install cache-loader thread-loader -D
Copy the code

Use thread-loader to parse resources

How it works: Each time webPack parses a module, Thread-Loader assigns it and its dependencies to the worker thread


module: {
        rules: [{test: /\.js$/,
                use: [
                     {
                         loader: 'thread-loader'.options: {
                             workers: 3}},'cache-loader'.// With cacheDirectory, you can cache compilation results to avoid multiple compilations;
                     'babel-loader',]},]}Copy the code

Multi-process/multi-instance: parallel compression

Method 1: Use the parallel ugliffe -plugin

plugins:[
 new ParallelUglifyPlugin({
    uglifyJS:{
        output:{
            beautify:false,
            comments:false
        },
        compress:{
            warning:false,
            drop_console:true,
            collapse_vars:true,
            reduce_vars:true
        }
    }
 })
]
Copy the code

Uglifyjs-webpack-plugin Enables the PARALLEL parameter

plugins:[
 new UglifyJsPlugin({
    uglifyOptions:{
        warning:false
    },
    parallel:true
 })
]
Copy the code

Terser-webpack-plugin enables the PARALLEL parameter

optimization:{
    minimizer: [new TerserPlugin({
            parallel:4}})]Copy the code

Narrow your Build goals

Purpose: Build as few modules as possible

For example, babel-loader does not resolve node_modules

rules: [
            {
                test: /\.js$/,
                exclude: 'node_modules'.// Ignore node_moudles to avoid compiling code already compiled in third-party libraries;
                use: [
                     'babel-loader',]}]Copy the code

Reduce file search scope

Optimize the resolve.extensions configuration

Use Aliases wisely

resolve: {
    alias: {
         'components': path.resolve(__dirname, './src/components'),
         'util': path.resolve(__dirname, './src/util'),},extensions: ['.js']}Copy the code

noParse

In Webpack, the loader we need to use is configured under module.rules, where modules in the WebPack configuration are used to control how different types of modules in a project are handled.

In addition to the module.rules field used to configure the Loader, there is also a module.noParse field used to configure which module file contents do not need to be parsed. For some large third-party libraries that do not need to resolve dependencies (i.e., no dependencies), you can configure this field to improve the overall build speed.

Import mechanisms such as import, require, and define cannot be used in module files that are ignored using noParse.

module.exports = {
  // ...
  module: {
    noParse: /jquery|lodash/.// Regular expressions

    // Or use function
    noParse(content) {
      return /jquery|lodash/.test(content)
    },
  }
}
Copy the code

Image compression

Use: configure image-webpack-loader


{
        test: /\.(png|jpg|gif|jpeg)$/,
        use: [
            {
                loader: 'file-loader'.options: {
                    name: '[name]_[hash:8].[ext]'}}, {loader: 'image-webpack-loader'.options: {
                  mozjpeg: {
                    progressive: true.quality: 65
                  },
                  optipng: {
                    enabled: false,},pngquant: {
                    quality: '65-90'.speed: 4
                  },
                  gifsicle: {
                    interlaced: false,},webp: {
                    quality: 75}}}Copy the code

How to delete useless CSS?

PurifyCSS: Iterate through the code to identify the CSS classes that have been used. This should be used in conjunction with the mini-CSs-extract-plugin

That is, extract to A CSS file and then use PurifyCSS

npm install purgecss-webpack-plugin

const PurgecssPlugin = require('purgecss-webpack-plugin');
plugins:[
    new MiniCssExtractPlugin({
            filename: '[name]_[contenthash:8].css'
    }),
    new PurgecssPlugin({
        paths: glob.sync(`${path.join(__dirname, 'src')}/ * * / * `,  { nodir: true})})]Copy the code

Construction volume optimization

We can use dynamic Polyfill -> Polyfill Service

Principle: Identify User Agent and deliver different polyfills

How to use dynamic Polyfill Service

Polyfill. IO :(added to index.html)

<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
Copy the code

Summary of volume optimization

Scope Hoisting

Tree-shaking

Separation of common resources

Image compression

Dynamic Polyfill

The principle of article

Loader development

//raw-loader.js
module.exports = function(source) {
    const json = JSON.stringify(source)
        .replace('foo'.' ')
        .replace(/\u2028/g.'\\u2028')
        .replace(/\u2029/g.'\\u2029');
    return `export default ${json}`;
}
Copy the code

Loader-runner replaces foo in specified file with null;

npm install loader-runner -D

Loader-runner usage details can be found on Github

const { runLoaders } = require('loader-runner');
const fs = require('fs');
const path = require('path');

runLoaders({
    resource: path.join(__dirname, './src/demo.txt'),// Resource path
    loaders: [{loader: path.join(__dirname, './src/raw-loader.js'),// Specify the loader path}].context: {
        minimize:true
    },
    readResource: fs.readFile.bind(fs)
}, (err, result) = > {
    err ? console.log(err) : console.log(result);
});
Copy the code

Obtain loader parameters

npm install loader-utils -D
Copy the code

Obtain the value by 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

The error is thrown directly by the loader and 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();
    this.cacheable(false)
    // No callback -> return synchronous results
    // if (callback) { ... }
    callback(null, input + input);
};
Copy the code

The first argument to this.async() is also an Err object, and the second argument is data

Use caching in the Loader

Loader caching is enabled by default in Webpack

You can use this.cacheable(false) to turn off caching

Cache condition: The loader result has a certain output with the same input

A dependent loader cannot use the cache

How does loader export files?

File write via 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__ + The ${JSON.stringify(url)}; `;
    return `export default ${path}`;
};
Copy the code

InterpolateName method is used to replace placeholders, __webpack_public_path__ is a global variable of Webpack

The plugin development

Plug-ins do not have a separate runtime environment like loader and can only run within WebPack

Build the plug-in running environment

const path = require('path');
const MyPlugin = require('./plugins/myPlugin');// Path during plug-in development

module.exports = {
    entry: './src/index.js'.output: {
        path: path.join(__dirname, 'dist'),
        filename: 'main.js'
    },
    mode: 'production'.plugins: [
        new MyPlugin({
            name: 'myname'}})]Copy the code

Develop the simplest plug-in possible

module.exports = class MyPlugin { MyPlugin is the name of the plugin
    constructor(options){
        this.options = options
    }
    apply(compiler){ // must be apply
        console.log('My plugin executed');
        console.log('My Plug-in Configuration Items'.this.options)
    }
}
Copy the code

Error handling of plug-ins

The parameter verification phase can be thrown directly as a throw

throw new Error("ErrorMessage ")Copy the code

If the hooks logic is already in place, it can be received through warnings and errors of the Compilation object

compilation.warnings.push("warning");
compilation.errors.push("error");
Copy the code

Plug-in file writing

The file generation phase webpack calls emit hooks so we can listen to the emit phase for action

File is written to use [webpack – sources] ((www.npmjs.com/package/web)…

const { RawSource } = require("webpack-sources");
module.exports = class DemoPlugin {
    constructor(options) {
    this.options = options;
    }
    apply(compiler) {
        const { name } = this.options;
        compiler.plugin("emit".(compilation, cb) = > {
            compilation.assets[name] = new RawSource("demo");// Demo is the file content, and name is the file namecb(); }); }};Copy the code

conclusion

I believe this article should have covered the most commonly used points of Webpack and how to optimize, interested partners can communicate in the article below