Summary of webpack2

This article github warehouse: https://github.com/Rynxiao/webpack2-learn

Migration from v1 to v2

1. Configuration type

In webpack1, configuration is done mainly by exporting a single object. For example, the following configuration:

// Export mode of webpack1
module.export = {
    entry : 'app.js',
    output : { */... */},
    /* ... */
};

In webpack2, however, there are three flexible configurations for different scenarios.

1.1 Export different configuration files through different environment variables

// There are two ways to pass the current value, one is to simply pass a string, the other is to pass an object.
// For example, the webpack --env production console prints'production', which is a string.
// When called in this way: webpack --env.production --env.size 60, the console prints {production: true, size: 60}

var path = require('path'),
    webpack = require('webpack'),
    UglifyJsPlugin = new webpack.optimize.UglifyJsPlugin(),
    plugins = [];

module.exports = function(env) {

    console.log(env);
  
    if (env === 'production') {
        plugins.push(UglifyJsPlugin);
    }

    return {
        entry : path.resolve(__dirname, 'js/app.js'),
        output : {
            path : path.resolve(__dirname, 'build'),
            filename : '[name].bundle.js'
        },
        module : {
            rules : [
                { 
                    test : /\.js|\.jsx$/, 
                    loader : 'babel-loader', 
                    options : {
                        presets : ["es2015", "react"]
                    } 
                },
                { 
                    test : /\.css$/,
                    use : ['style-loader', 'css-loader']
                },
                {
                    test : /\.less$/,
                    use : ['style-loader', 'css-loader', 'less-loader']
                }
            ]
        },
        plugins : plugins
    };
}

// Configure two commands in package.json
{
    "dev" : "webpack",
    "build" : "webpack --env production"
}

Specific production environment construction methods can refer to the official website. production

1.2 Export configuration files by promise

The application scenario of this method is that in some cases, we can't get the configuration parameters needed by the configuration file temporarily, such as the name of the file to be configured, etc. Perhaps this is an asynchronous operation, through promise, we can get the configuration variables after the asynchronous operation, and then execute the configuration file.

// In this case, a second later, the configuration file is returned and executed

var path = require('path');

module.exports = () => {
    return new Promise((resolve, reject) => {
        console.log('loading configuration ...');
        setTimeout(() => {
            console.log('loading completed!');
            resolve({
                entry : path.resolve(__dirname, 'js/app.js'),
                output : {
                    path : path.resolve(__dirname, 'build'),
                    filename : '[name].bundle.js'
                },
                module : {
                    rules : [
                        { 
                            test : /\.js|\.jsx$/, 
                            loader : 'babel-loader', 
                            options : {
                                presets : ["es2015", "react"]
                            } 
                        },
                        { 
                            test : /\.css$/,
                            use : ['style-loader', 'css-loader']
                        },
                        {
                            test : /\.less$/,
                            use : ['style-loader', 'css-loader', 'less-loader']
                        }
                    ]
                },
            });
        }, 1000);
    });
}

1.3 Packing multiple configuration files at the same time

Web Pack 1 can only export a single configuration file. In webpack 2, multiple configuration files can be packaged at the same time, which means that multiple entry files can be packaged. When multiple pages are packaged, there is no need to execute packaging commands for each single page.

// config-amd.js
var path = require('path');

module.exports = {
    entry : path.resolve(__dirname, 'js/app.js'),
    output : {
        path : path.resolve(__dirname, 'build'),
        filename : '[name].amd.js',
        libraryTarget : 'amd'
    },
    module : {
        rules : [
            { 
                test : /\.js|\.jsx$/, 
                loader : 'babel-loader', 
                options : {
                    presets : ["es2015", "react"]
                } 
            },
            { 
                test : /\.css$/,
                use : ['style-loader', 'css-loader']
            },
            {
                test : /\.less$/,
                use : ['style-loader', 'css-loader', 'less-loader']
            }
        ]
    }
};

// config-commonjs.js
var path = require('path');

module.exports = {
    entry : path.resolve(__dirname, 'js/app.js'),
    output : {
        path : path.resolve(__dirname, 'build'),
        filename : '[name].commonjs.js',
        libraryTarget : 'commonjs'
    },
    module : {
        rules : [
            { 
                test : /\.js|\.jsx$/, 
                loader : 'babel-loader', 
                options : {
                    presets : ["es2015", "react"]
                } 
            },
            { 
                test : /\.css$/,
                use : ['style-loader', 'css-loader']
            },
            {
                test : /\.less$/,
                use : ['style-loader', 'css-loader', 'less-loader']
            }
        ]
    }
};

// webpack.config.js
var configAmd = require('./config-amd.js'),
    configCommonjs = require('./config-commonjs.js');

module.exports = [
    configAmd,
    configCommonjs
]

2. resolve correlation

2.1 extensions Suffix Extension

In webpack2, you don't need to write an empty string by default. If you don't configure this option, the default suffix name is ['.js','.json'], so you can write import'some'directly when you need to use import'some.js'.

If you don't want to turn on the automatic suffix, you need to configure enforce Extension: true in solution, for example:

var path = require('path');

module.exports = {
    entry : // ....,
    // ...
    resolve : {
        enforceExtension : true
    }
};

At this point, if js/text.js is referenced in js/app.js, an error will be reported.

// Error
import './text';

// Right
import './text.js';

2.2 Root/fallback/modules Directories File Location

Configuring these three attributes in webapck1 solution tells webpack to look for folders when introducing modules. In webpack2, a separate attribute module is replaced directly, and node_modules are searched first by default (note, this is a relative location).

// config
resolve: {
    // Root: path. join (_dirname, "src") webpack1 way
    modules : [
        path.join(__dirname, "src"),    // Priority over node_modules/search
        "node_modules"
    ]
}

// Modify js/app.js
// In the JS folder, add a lodash.js. If you configure the modules above, you will load our own lodash library first.
import '../css/style.less';
import _ from 'lodash';

console.log(_.isObject([1, 2, 3]));
document.getElementById('container').textContent = 'APP';

// js/lodash.js
export default {
    isObject(a) {
        console.log('this is my lodash library!');
        return a && typeof a === 'object';
    }
}

The results are as follows:

3. module correlation

3.1 module.rules to replace module.loaders

The old loader configuration was superseded by a more powerful rules system, which allows configuration of loaders and more. For compatibility reasons, the old module.loaders syntax is still valid and the old names are parsed. The new naming conventions are easier to understand and are a good reason to upgrade the configuration to using module.rules.

The main idea is that the new name is easier to understand (for me, it's an English word: - D), and it's compatible with the old way, that is to say, you can still write module.loaders.

module : {
    // webpack1 way
    // loaders : [...]
    
    // now
    rules : [
        ...
    ]
}

3.2 module[*].loader

If only one loader is needed for the module to be loaded, then you can use the keyword loader directly; if the module to be loaded needs multiple loaders, then you need to use the keyword use, which can configure parameters in each loader. The code is as follows:

module : {
    rules : [
        { test : /\.js|\.jsx$/, loader : 'babel-loader' },
      
        /* If there are parameters that need to be passed to the current loader, then continue to add options keywords, such as:
          { 
            test : /\.js|\.jsx$/, 
            loader : 'babel-loader', 
            options : { presets : [ 'es2015', 'react' ] } 
          }
        */
      
        {
            test : /\.css$/,
            // webpack1 way
            // loader : 'style!css'
          
            use : [ 'style-loader', 'css-loader' ]
        },
        {
            test : /\.less$/,
            use : [
                'style-loader',     // The default is equivalent to {loader:'style-loader'}
                {
                    loader : 'css-loader',
                    options : {
                        modules : true
                    }
                },
                'less-loader'
            ]
        }
    ]
}

3.2 Cancel automatic addition of-loader suffix

Writing loader s before is usually like this:

loader : 'style!css!less'
// equals to
loader : 'style-loader!css-loader!less-loader'

The - loader suffix is automatically added, and is no longer automatically added in webpack2. If you need to maintain the same way as webpack1, you can add an attribute to the configuration as follows:

module.exports = {
    ...
    resolveLoader : {
        moduleExtensions : ["-loader"]
    }
}

// Then you can continue to write like this, but it's officially recommended.
// The main reason for not recommending is to take care of the novice. Writing directly will confuse the newly contacted children's shoes.
// github.com/webpack/webpack/issues/2986
use : [ 'style', 'css', 'less' ]

3.3 json-loader built-in

If you want to load a JSON file, you no longer need to configure json-loader, because webpack2 is built-in.

4. plugins related

4.1 Uglify JsPlugin Code Compression Plugin

Warning and sourceMap in the compression plug-in no longer default to true, if you want to turn it on, you can configure it this way

plugins : [
    new UglifyJsPlugin({
        souceMap : true,
        warnings : true
    })
]

4.2 ExtractText Webapck Plugin Text Extraction Plugin

It's mainly a change in writing. If you want to use it with webpack2, you need version 2.

// webpack1 way
modules : {
    loaders : [
        { 
            test : /\.css$/, 
            loader : ExtractTextPlugin.extract('style-loader', 'css-loader', { publicPath : '/dist' })
        }   
    ]
},
plugins : [
    new ExtractTextPlugin('bunlde.css', { allChunks : true, disable : false })
]

// webapck2 way
modules : {
    rules : [
        { 
            test : /\.css$/, 
            use : ExtractTextPlugin.extract({
                fallback : 'style-loader',
                use : 'css-loader',
                publicPath : '/dist'
            })
        }
    ]
},
plugins : [
    new ExtractTextPlugin({
        filename : 'bundle.css',
        disable : false,
        allChunks : true
    })
]

5. debug mode of Loaders

To turn on the debugging mode of loaders in webpack1, debug options need to be loaded, which are no longer used in webpack2, and will be deleted in webpack3 or later. If you want to continue using it, please use the following words:

// webpack1 way
debug : true

// webapck2 way 
// webapck2 moves loader debugging to a plug-in
plugins : [
    new webpack.LoaderOptionsPlugin({
        debug : true
    })
]

6. Change loading mode on demand

6.1 import() mode

In webpack1, if you want to load a module on demand, you can use require.ensure([], callback). In webpack2, ES2015 loader defines an import() method instead of the previous one, which returns a promise.

// Add a new main.js in the JS directory
// js/main.js
console.log('main.js');

// webpack1 way
require.ensure([], function(require) {
    var _ = require('./lodash').default;
    console.log(_);
    console.log('require ensure');
    console.log(_.isObject(1));
});

// webpack2 way
// In this way, polyfill of promise is required
// Two ways:
// 1. npm install es6-promise --save-dev
//    require('es6-promise').polyfill();
//
// 2. babel mode, configuring babel plug-ins in webpack
//    npm install babel-syntax-dynamic-import --save-dev
//    options : {
//        presets : ['es2015'],
//        plugins : ['syntax-dynamic-import']
//    }
import('./lodash').then(module => {
    let _ = module.default;
    console.log(_);
    console.log('require ensure');
    console.log(_.isObject(1));
});

The chunk file you get is as follows:

6.2 Dynamic Expressions

You can dynamically transfer parameters to load the modules you need, such as:

function route(path, query) {
    return import(`./routes/${ path }/route`)
        .then(route => { ... })
}

7. Thermal replacement is simpler

webpack2 provides a simpler way to use hot replacement capabilities. Of course, if you want to start the hot replacement function with node, you can still follow the way in webpack1.

npm install webpack-dev-server --save-dev

// webpack.config.js
module.exports = {
    // ...,
    devServer : {
        contentBase : path.join(__dirname, 'build'),
        hot : true,
        compress : true,
        port : 8080,
        publicPath : '/build/'
    },
    plugins : [
        new webpack.HotModuleReplacementPlugin()
    ]
}

Talking about V2 Version

This article mainly introduces some things that were ignored in webpack1 and added in v2 version.

1. caching

In order not to load the same resources repeatedly, browsers add caching function. Usually if the requested file name does not change, the browser assumes that you request the same resource, so the loaded file is taken from the cache, which causes a problem. In fact, the content of your file has changed, but the file name has not changed, so if you still load the file from the cache, something will happen.

In the past, the traditional method was to add a version number to each file, for example:

app.js?version=1
app.css?version=1

Add 1 to the current version number for each change, but if you update all version numbers every time there is only one change in the content of the file, the cache will fail for the browser and need to be reloaded, which is wasteful. Then, combined with the data digest algorithm, the version number is generated according to the content of the file, so the current version may be like this.

// before
app.js?version=0add34
app.css?version=1ef4a2

// after
// change app.js content
app.js?versoin=2eda1c
app.css?version=1ef4a2

You can see how to deploy the front-end code How Big Companies Develop and Deploy Front-end Code

webpack provides us with a simpler way to generate unique hash values for each file. In order to find the corresponding version number of the entry file, we need to obtain statistical information, such as:

{
  "main.js": "main.facdf96690cca2fec8d1.js",
  "vendor.js": "vendor.f4ba2179a28531d3cec5.js"
}

At the same time, if we use html-webpack-plugin, we don't need to be so troublesome. He will automatically bring the corresponding version to the file. See previous writing for specific comments. Web Pack 1 knowledge combing So our configuration now looks like this:

npm install webpack-manifest-plugin --save-dev

// webpack.config.js
module.exports = {
    entry : { /* ... */ },
    output : {
        path : path.resolve(__dirname, 'build-init'),
        filename : '[name].[chunkhash].js',
        chunkFilename : '[name].[chunkhash].js'
    },
    module : {
        // ...
    },
    plugins : [
        new htmlWebpackPlugin({
            title : 'webpack caching'
        }),
        new WebpackManifestPlugin()
    ]
}

html introduction

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>webpack caching</title>
  </head>
  <body>
  <div id="container"></div>
  <script type="text/javascript" src="main.facdf96690cca2fec8d1.js"></script><script type="text/javascript" src="vendor.f4ba2179a28531d3cec5.js"></script></body>
</html>

WARNNING:

Don't use [chunkhash] in a development environment because it increases compilation time. Separate the configuration of development and production mode, and use the filename of [name].js in development mode and [name].[chunkhash].js filename in production mode.

To minimize the file size, webpack uses identifiers instead of module names. At compilation time, a chunk block named manifest is generated and put into entry. So when we update some content, because hash is worth changing, it will cause manifest block file to be regenerated, which will not achieve the purpose of long-term caching. Web pack provides a plug-in, ChunkManifest Web pack Plugin, which extracts manifest mappings into a separate json file so that only references are needed in the manifest block without regeneration, so the final configuration is as follows:

var path = require('path'),
    webpack = require('webpack'),
    htmlWebpackPlugin = require('html-webpack-plugin'),
    ChunkManifestWebpackPlugin = require('chunk-manifest-webpack-plugin'),
    WebpackChunkHash = require('webpack-chunk-hash');

module.exports = {
    entry : {
        main : path.resolve(__dirname, 'js/app.js'),
        vendor : path.resolve(__dirname, 'js/vendor.js')
    },
    output : {
        path : path.resolve(__dirname, 'build'),
        filename : '[name].[chunkhash].js',
        chunkFilename : '[name].[chunkhash].js'
    },
    module : {
        // ...
    },
    plugins : [
        new webpack.optimize.CommonsChunkPlugin({
            name : ['vendor', 'manifest'],
            minChunks : Infinity
        }),
        new webpack.HashedModuleIdsPlugin(),
        new WebpackChunkHash(),
        new htmlWebpackPlugin({
            title : 'webpack caching'
        }),
        new ChunkManifestWebpackPlugin({
            filename : 'chunk-mainfest.json',
            manifestVariable : 'webpackManifest',
            inlineManifest : true
        })
    ]
}

tips: If it's not clear, it's clear to compare the difference between Chunk Manifest Web pack Plugin and No. You can see this difference in the code folder caching of this article

Keywords: Javascript Webpack less JSON React

Added by David4321 on Thu, 13 Jun 2019 21:09:08 +0300