Tree Shaking

Tree Shaking takes advantage of ES6. The essence of it is to analyze static module code, so you need to determine during the build process which modules can be used and which modules do not need to be distinguished. Remove unwanted code during the UGLIFy phase by identifying code that is not needed.

Tree shaking works by removing unreferenced code from the packaging process if a module has multiple methods and only one of them is used during the build process and putting only the methods used into the bundle.Copy the code

This is done by enabling mode:’ Production ‘. Only ES6 syntax is supported. Commonjs (require) syntax is not supported.

Scope Hoisting

The idea is to put all module code into a function scope in the order it was introduced, and then rename some variables appropriately to prevent variable name conflicts. This reduces the function declaration code and memory overhead.

SplitChunksPlugin

Perform generic code pull-out by pumping the same code into a shared block or script lazy loading to make the initial download smaller

Lazy script

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

speed-measure-webpack-plugin

The speed-measure-webpack-plugin plug-in can be used to analyze the execution time of each loader and plugin conveniently, which can effectively analyze the total packaging time and plug-in and Loader time, and provide effective information.

npm i speed-measure-webpack-plugin -D
Copy the code
//webpack.prod.js
const SpeedMeasureWebpackPlugin = require('speed-measure-webpack-plugin')
const smp = newSpeedMeasureWebpackPlugin() modue.exports = smp.wrap({... })Copy the code

webpack-bundle-analyzer

Webpack-bundle-analyzer is used for visual analysis of packaged projects, which can analyze the dependent third-party module file size and business component code size, and then analyze whether large files can be extracted by component replacement or CDN.Copy the code
npm i webpack-bundle-anaylzer -D
Copy the code
//webpack.prod.js
const WebpackBundleAnalyzer = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports = {
    plugins: [
        new WebpackBundleAnalyzer()
    ]
}
Copy the code

HappyPack parses resources

The HappyPack resolution principle is that every time WebPack parses a module, HappyPack assigns it and its dependencies to the worker thread.

HappyPack initializes and creates the HappyThreadPool thread pool after Executing compiler.run in WebPack. The thread pool allocates threads for module building tasks, so multiple threads will run in a thread pool and process modules or dependencies. After the processing is complete, the resources are fabled to the main HappyPack process to complete the build.

npm i happypack -D
Copy the code
//webpack.prod.js
const Happypack = require('happypack')
module.exports ={
    module: {
        rules: [{test: /.js$/,
                 use: [
                    'happypack/loader'
                     /*'babel-loader'*/]},]},plugins: [
        new Happypack({
           loaders: ['babel-loader']]}})Copy the code

Thread-loader Parsing resources (recommended)

Thread-loader is officially provided by Webpack4. Its principle is also that each time webpack resolves a module, thread-Loader assigns it and its dependencies to worker threads to achieve multi-threaded construction.

npm i thread-loader -D
Copy the code
// webpack.prod.js
module.exports = {
    module: {rules:[
            {
                test: /.js$/,
                exclude: /node_modules/,
                use: [
                   {
                       loader: 'thread-loader'.options: {
                           workers:3}},'babel-loader']},]}}Copy the code

Parallel compression

parallel-uglify-plugin

const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin')
module.exports = {
    plugins: [
        new ParallelUglifyPlugin({
        UglifyJS: {output: {
                beautify: false.comments: false
            },
            compress: {
                warnings: false.drop_console: true.collapse_vars: true.reduce_vars: true}}})]}Copy the code

Uglifyjs-webpack-plugin Enables the PARALLEL parameter

const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
    plugins: [
        new UglifyJsPlugin({
         uglifyOptions: {warnings: false.parse: {},
            compress: {},
            mangle:true.output: null.nameCache: null.ie8: false.keep_fnames: false
            },
            parallel: true}})]Copy the code

Terser-webpack-plugin Enables parallel parameter (recommended)

The difference between terser-webpack-plugin and Uglifyjs-webpack-plugin is that the Terser-webpack-plugin supports ES6 syntax compression, which is recommended by default for Webpack4.

npm i terser-webpack-plugin -D
Copy the code
//webpack.prod.js
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
    optimization: {
         minimizer: [
             new TerserPlugin({
                  parallel: true}}})]Copy the code

The subcontract

Set the Externals

It is introduced through CDN and is not imported into the bundle to realize the separation of the base library. However, this can also lead to multiple requests because one base library points to one CDN, requiring multiple scripts to be imported. If splitChunks are used to implement subcontracting, the analysis of basic packages will be carried out during the construction of the project, thus increasing the construction time

html-webpack-externals-plugin

npm i html-webpack-externals-plugin -D
Copy the code
//webpack.prod.js
const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin')
module.exports = {
    plugins: [
        new HtmlWebpackExternalsPlugin({
        externals:[
            {
                module:'react'.entry: 'https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/cjs/react.production.min.js'.global: 'react'}, {module:'react-dom'.entry: 'https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/cjs/react-dom.production.min.js'.global: 'ReactDom',}]})]}Copy the code

Precompiled resource modules :DLLPlugin + DllReferencePlugin recommended)

DLLPlugin extracts multiple base packages or business base packages and generates a package file and manifest.json(a description of the separated packages). In practice, DllReferencePlugin can be used to associate separate packages with manifest.json references.

Create webpack.dll.js and configure the scripts command to use DLLPlugin for subcontracting

//package.json{ "scripts":{ "dll": "webpack --config webpack.dll.js" }}
Copy the code
//webpack.dll.jsconst path = require('path')const webpack = require('webpack')module.exports = { entry: { library: 'react', 'react-dom']}, output: {filename: '[name]_[chunkhash:8].dll. Path. join(__dirname, './build/library'), // Avoid dist cleanup library: '[name]'}, plugins: [new webpack.DllPlugin({// provide manifest reference name: '[name]_[hash:8]', path: path.join(__dirname, './build/library/[name].json') }) ]}
Copy the code

Use the DllReferencePlugin to reference manifest.json

//webpack.prod.jsmodule.exports = { plugins: [ new webpack.DllReferencePlugin({ manifest: require('./build/library/library.json') }) ]}
Copy the code

The cache

Enabling caching on build projects can speed up secondary builds. If the cache can be enabled through babel-loader, the contents of the cache can be directly read when the Babel converts JS and JSX syntax next time. UglifyJsPlugin or TerserWebpackPlugin can be used to enable caching in the code compression phase. Cache-loader or hard-source-webpack-plugin can improve the cache during module transformation.

The cache contents for node_modules can be in the. Cache directory.

//webpack.prod.jsconst HardSourceWebpackPlugin = require('hard-source-webpack-plugin')module.exports = { module: { rules:[ { test: /.js$/, use:[ { loader: 'thread-loader', options: {workers:3}}, 'babel-loader? CacheDirectory =true']}]}, plugins: [ new HardSourceWebpackPlugin() ], optimization:{ minimizer: [ new TerserPlugin({ parallel: true, cache: True // Enable caching})]}}
Copy the code

Narrow your Build goals

  • Build fewer modules: for some third-party modules, we don’t need to do further parsing. For example, babel-loader doesn’t need to parse some third-party packages such as UI library for node_modules, because its quality is guaranteed

    module.exports = {    module: {rules:[         test: /.js$/,         loader: 'happypack/loader'.exclude: 'node_modules' //include:path.resolve('src') ] }}
    Copy the code
  • Reduce the scope of file search: optimize the resolve.modules configuration to reduce the level of module search; Optimized resolve.mainFields configuration, optimized entry configuration; Optimize the resolve.extensions configuration to optimize the suffix for finding file pairs. Use aliases properly.

    //webpack.prod.jsmodule.exports ={ resolve: { alias: { 'react': path.resolve(__dirname,'./node_modules/react/umd/react.production.min.js'), 'react-dom': path.resolve(__dirname,'./node_modules/react/umd/react-dom.production.min.js') }, extensions: ['.js'], mainFields: ['main'] }}
    Copy the code

Construction volume optimization

Tree shaking

Delete invalid CSS

  • PurifyCSS: Traverses code to identify used CSS classes by marking used and unused classes. At present, purifyCSS-Webpack has stopped updating and needs to be replaced with Purgecss-Webpack-Plugin combined with mini-CSs-extract-Plugin in Webpackage 4.
npm i purgecss-webpack-plugin -D
Copy the code
//webpack.prod.jsconst MiniCssExtractPlugin = require('mini-css-extract-plugin'); const PurgecssPlugin = require('purgecss-webpack-plugin')const PATHS = { src: path.join(__dirname,'src')}module.exports ={ module:{ rules: [ { test: /.css$/, use: [ MiniCssExtractPlugin.loader, 'css-loader' ] }, ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name] _ [8] contenthash: CSS'}), new PurgecssPlugin ({path: glob. Sync (` ${PATHS. SRC} / * * / * `, {nodir: true}) / / absolute path}),]}
Copy the code
  • Uncss: HTML needs to be loaded via jsDOM and all styles parsed through PostCSS so that document.querySelector can be used to identify selectors that do not exist in HTML files.

Image compression

image-webpack-loader

Node-based imagemin can be implemented by configuring image-Webpack-Loader. Imagemin can provide customization options, such as introducing more third-party optimization plug-ins, PngQuant, etc., and supports compression of multiple image formats

npm i image-webpack-loader -D
Copy the code
module.exports ={    module: {rules:[            {               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 will disable opTIPng opTIPng: {enabled: false,}, pngQuant: {quality: [0.65, 0.90], speed: 4 }, gifsicle: { interlaced: false, }, // the webp option will enable WEBP webp: { quality: 75 } } }, ] }, ] }}
Copy the code

Set dynamic Polyfill

For some ES6 syntaxes, different browsers may require different compatibility handling, and Polyfill provides a compatible approach. However, a full Polyfill would need to handle a lot of compatibility syntaxes, resulting in a bulky build.

What happens when you run WebPack?

When you run webpack with NPM scripts, NPM actually runs webpack by having the command line tool go to node_modules/. Bin and look for webpack. CMD or webpack.sh files, execute if they exist, and throw an error if they don’t. Node_modules/bin directory with this command, if the local installation, you need to rely on the configuration package. The designated bin field in json, so to find the entrance to the actual file is node_modules/webpack/bin/webpack. Js

  1. So, NPM scripts runs the command line and eventually looks for webpack-cli/webpack-command through webpack and executes the CLI.
  2. Webpack analyzes commands that do not need to be compiled. Commands such as init and info do not instantiate Webpack objects, and WebPack does not need to go through the build compilation process.
  3. Analyze command line parameters and convert them to compile configuration items
  4. Reference WebPack, compile and build from the configuration items
  5. So, the result of the Webpack-CLI execution is to convert the configuration file to the command line parameters and eventually generate the configuration option parameter options. The build process is then executed by instantiating the WebPack object based on the configuration parameters

Webpack plugin mechanism

  1. If the CLI is followed, call Webpack – CLI converts the parameter grouping into compilation configuration items recognized by WebPack, and finally call Webpack again to execute the Compiler instance
  2. As you can see from the Webpack source, the Webpack function creates the Compiler instance.
  3. As can be seen from the compiler. js source code, the core object Compiler is the inheritance and extension of Tapable.
  4. Compiler instance created by Compilation. Compilation is generally inherited to Tapable.
  5. Compiler and Compilation, the core object of Webpack, are inherited to Tapable to realize the association between Tapable and Webpack
  6. Tapable mainly exposes hook functions and provides hooks for plug-ins to mount. In other words, Tapable mainly controls the publishing and subscribing of hook functions and controls the plug-in system of WebPack.
  7. Webpack can be thought of as an event-based programming paradigm, with a series of plug-ins running the process. The corresponding event operations are performed by listening to the compiler defined on the plug-in and the key nodes of compilation.

Tapable

Tapable is a publish and subscribe library that provides hook functions for publish and subscribe. It is also an implementation of the WebPack plug-in system.

const {	SyncHook, // SyncBailHook, // SyncWaterfallHook, // SyncLoopHook, // AsyncParallelHook, // AsyncSeriesHook, // AsyncSeriesBailHook, // AsyncSeriesBailHook, // AsyncSeriesBailHook, // AsyncSeriesWaterfallHook} = require("tapable");
Copy the code
type methods
Hook Suffix for all hooks
Waterfall Synchronous method, but passes the value to the next function
Bail Fusing: When the function has any return value, the current execution of the function stops
Loop Listeners return true to continue the loop and undefine to end the loop
Sync Synchronized methods
AsyncSeries Asynchronous serial hook
AsyncParallel Asynchronous parallel execution hooks

The use of Tapable

  1. Since Tapable exposes class methods, create new Hook functions via new Hook functions. Where the class method takes an array parameter, options(optional). Class methods accept a common number of arguments, based on the passed arguments.

    Const hook = new SyncHook(["arg1", "arg2", "arg3"])Copy the code
  2. Hook publish and subscribe. Tapable provides methods for binding hooks synchronously and asynchronously, and both have methods for binding events and executing events

    Asynchronous synchronous binding: tapAsync/tapPromise/ TAP Binding: TAP Execute: callAsync/ PROMISE execute: callCopy the code
  3. Hook:

    Const hook1 = new SyncHook(["arg1", "arg2", Tap ('hook1',(arg1,arg2,arg3)=>console.log(arg1,arg2,arg3))
    Copy the code

    Create a Car class and define synchronous hooks, asynchronous hooks, and bind and execute methods on the hooks.

    const {    SyncHook,    AsyncSeriesHook} = require('tapable')class Car {    constructor(){        this.hooks = {            accelerate: new SyncHook(['newspeed']),            brake: new SyncHook(),            calculateRoutes: new AsyncSeriesHook(["source"."target"."routesList"])}}}const myCar = new Car()Tap ("WarningLampPlugin", () = > console. The log (' WarningLampPlugin)) / / binding synchronization hooks and mass participation myCar. Hooks. Accelerate. Tap (' LoggerPlugin ', newSpeed => console.log('Accelerating To '+ newSpeed)) / / bind an asynchronous myCar. Promise hook hooks. CalculateRoutes. TapPromise (' calculateRoutes tapPromise', (source, target, routesList)=>{ console.log('source',source) return new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log(`tapPromise to ${source} ${target} ${routesList}`) resolve() },1000) })}) / / synchronization hooks myCar. Hooks. The brake. The call () myCar. Hooks. Accelerate. Call myCar. (10) / / execute asynchronous hook hooks. CalculateRoutes. Promise (' Async ', ' hook','demo').then(()=>{ console.log('succ')},err=>{ console.err(err)})
    Copy the code

Webpack compilation process analysis

The compilation stage of Webpack can be divided into preparation stage, which converts options into options configuration items recognized by Webpack. In the construction phase, Compiler provides hook functions for each construction phase, code optimization phase, and Compilation provides the process of module optimization construction.

  1. Preparation stage

    The Webpack preparation phase mounts some plug-ins to the Compiler instance, along with some initialization of the EntryOptionPlugin. Compiler is then entered, Compilation is completed, and the NormalModuleFactory and ContextModuleFactory factory functions are created, and finally the Compiler.run stage is entered.

  2. The construction phase

    1. Compiler is instantiated to create Compilation objects, NormalModuleFactory, ContextModuleFactory factory methods, run methods, and compile when beforeRun is triggered. Among them, the Compiler process related hooks are: beforeRun/run, beforeCompile/AfterCompile, make afterEmit/emit, done, etc., and hooks are related to the listening watchRun, watchClose
    2. Compilation is the process of building and optimizing a module. Compilation hooks :buildModule, failModule, succeedModule resource generation: moduleAsset, chunkAsset Optimize, afterOptimzeModules, etc.
  3. Files are generated

    Finally, after the Compilation has completed seal and optimization, it returns to Compiler to execute emitAssets and output the content to disk.

  4. To summarize, Compilation of Webpack follows the sequence of hook calls: initialize EntryOptions, enter Compiler.run, and hooks. Make calls the Compilation addEntry hook.

  5. Recursively analyze the dependencies from Entry, build each dependency module, resolve the location of the module beforeResolve, and then start building the module buildModule,

  6. Compile the module loaded by loader through normalModuleLoader, generate the AST abstract syntax tree, and then traverse the AST to collect dependencies on require and other calls.

  7. Finally, after all the dependencies are built, seal and optimization are performed, and the disk output is returned to Compiler.emit to complete the compilation process.

Loader Compilation Principle

Loader is essentially a JavaScript method that passes in the source code, and the output returns the new source code

module.exports = function(source) {  return source}
Copy the code

Simple usage

  1. When a loader is used in a resource, the loader can only pass in one argument – the argument is a string containing the contents of the resource file
  2. The synchronous loader can simply return a value representing the converted module. Loader can also be used in more complex casesthis.callback(err, values...)Function that returns an arbitrary number of values. The error is either passed to thisthis.callbackFunction, or thrown into the synchronization loader.
  3. The loader returns one or two values. The first value is a string or buffer of JavaScript code. The second parameter value is SourceMap, which is a JavaScript object.

Complex usage

When chaining multiple Loaders, keep in mind that they will be executed in reverse order. Execute from right to left or bottom up, depending on the array format.

  • The last earliest loader call will pass in the original resource content.
  • The first loader is called last, with the expected outgoing JavaScript and source map (optional).
  • When an intermediate loader executes, the result of the previous loader is passed in.

So, in the following example, foo-loader is passed the raw resource, and bar-loader will receive the output of foo-Loader and return the final converted module and a source map (optional)