Introduction to Webpack

1.1 What is WebPack

Webpack is a front-end resource builder, a static Module Bundler.

In webpack’s view, all the front-end resource files (js/json/ CSS /img/less/…) Will be handled as modules. It performs static analysis based on the dependencies of modules and packages the corresponding static resources (bundles).

1.2 WebPack five core concepts

1.2.1 Entry

Entry: Indicates to WebPack which file is the Entry point to start packaging and analyze the build internal dependency graph.

1.2.2 the Output

Output: Indicates where webPack bundle Output goes and how to name it.

1.2.3 Loader

Loader: Allows Webpack to handle non-JS files such as style files and image files (Webpack only understands JS)

1. The Plugins

Plugins: Can be used to perform a wider range of tasks. Plug-ins can range from packaging optimizations and compression to redefining variables in the environment.

1.2.5 Mode

Mode: Instructs WebPack to use the configuration for the appropriate Mode.

options describe The characteristics of
development The value of process.env.node_env in DefinePlugin is set to development. Enable NamedChunksPlugin and NamedModulesPlugin. An environment where code can be debugged locally
production The value of process.env.node_env in DefinePlugin is set to production. Enable FlagDependencyUsagePlugin FlagIncludedChunksPlugin, ModuleConcatenationPlugin NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin and TerserPlugin. An environment that allows code optimization to come online

First experience of Webpack

2.1 Initial Configuration

  1. Json: NPM init
  2. Download and install Webpack: (For versions later than Webpack4, install webpack-CLI globally and locally.) For global installation, run CNPM I webpack webpack-cli -g for local installation, run CNPM I webpack webpack-cli -d

2.2 Compiling and Packaging Applications

After creating js files under SRC, you do not need to configure the webpack.config.js file. You can compile and package the files on the command line.

Instructions:

  • Development environment: Webpack./ SRC /index.js -o./build/ build. js –mode=development Webpack starts with./ SRC /index.js. After packaging, output to./build/built. Js overall packaging environment, is the development environment
  • Production environment: / SRC /index.js -o./build/ build. js –mode=production Webpack starts with./ SRC /index.js. After packaging, output to./build/built. Js overall packaging environment, is the production environment

Conclusion:

  1. Webpack itself can handle JS/JSON resources, but not CSS/IMG and other resources
  2. Production and development environments compile ES6 modularity into modularity recognized by browsers, but cannot handle the basic syntax of ES6 to ES5 (with the help of loader)
  3. The production environment has one more compressed JS code than the development environment

Basic configuration of Webpack development environment

Webpack.config. js is the webPack configuration file.

Function: Tells WebPack what to do (when you run a Webpack directive, it loads the configuration inside)

All build tools run on the NodeJS platform, and commonJS is the default modularity.

Development environment configuration is primarily about getting code to run. The following aspects should be considered:

  • Package style resources
  • Packaging HTML resources
  • Package image resources
  • Package other resources
  • devServer

Below is a simple development environment webpack.confg.js configuration file

Const {resolve} = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') // Pluginmodule. exports = {// webpack configure entry: './ SRC /js/index.js', // output: {// output // output filename: 'js/build.js', // __dirname is a variable of nodejs, which represents the absolute directory path of the current file: Resolve (__dirname, 'build'), // Output path to which all resource packages will be output}, // Loader configure module: {rules: [// detailed loader configuration // Different files must be configured for different Loader processing {// which files to match test: /\.less$/, // Which loader to use for processing use: [// loaders in the array are executed from right to left and from bottom to top. 'style-loader', // css-loader: Load the CSS file into the commonJS module, which contains the style string 'css-loader', // less-loader: < span style = "box-sizing: border-box; border-box: border-box; border-box: border-box; border-box: border-box; border-box: border-box; border-box: border-box; ['style-loader', 'css-loader'],}, {// url-loader: handle image resources / \. (JPG | PNG | GIF) $/, / / need to download url - loader file - loader loader: 'url - loader, the options: {// If the size of an image is less than 8KB, it will be processed by base64. // Base64 decodes the image locally on the client, so it reduces server pressure. If the image is too large, using base64 encoding will cause CPU call rate to increase, and the page loading time will change limit: 8 * 1024, // rename the image, [hash:10] : get the first 10 bits of the hash of the image, [ext] : get the original extension name of the file: '[hash:10].[ext]', // Problem: Because URl-Loader uses es6 modular parsing by default, while HTml-Loader imports images that are conmonjs, there is a problem with parsing: [object Module] // Solution: EsModule: false, outputPath: 'imgs',},}, {test: /\.html$/, // process the img image of the HTML file (responsible for importing img, which can be processed by the URl-loader) 'HTML - loader,}, / / packaging other resources (in addition to the HTML/js/CSS outside resources) {/ / | | js | CSS rule out HTML less | JPG | PNG | GIF file exclude: / \. | | js | (HTML CSS less | JPG | PNG | GIF) /, / / file - loader: handle other file loader: 'file - loader' options: {name: '[10] hash: [ext]', outputPath: 'media',}},],}, / / the plugin to configure plugins: [/ / HTML - webpack - the plugin: By default, an empty HTML file is created, automatically importing all the resources that are packaged for output (JS/CSS). If you want a structured HTML file, you can add a template new HtmlWebpackPlugin({// copy this. Template: './ SRC /index.html',}),], // Mode: DevServer: for automation, you don't have to reenter webPack every time you modify it (automatic compilation, automatic browser opening, automatic browser refresh) NPX webpack-dev-server: NPX webpack-dev-server: NPX webpack-dev-server: NPX webpack-dev-server: {// contentBase: resolve(__dirname, 'build'), // Start gZIP compress: true, // port: 3000, // open the browser automatically: true, }, }Copy the code

Most of these configurations are explained in the comments.

  • Two instructions to run the project: webpack will output the package results (build folder); NPX Webpack-dev-server will only compile the package in memory, no output
  • (plugin must be introduced before use) 2. Use (loader) plugins: 1. Download 2. Introduce 3. Use

Basic configuration of Webpack production environment

The following aspects need to be considered when configuring a production environment:

  • Extract CSS into individual files
  • CSS Compatibility
  • Compress CSS
  • Js syntax checking
  • Js compatibility processing
  • Js compressed
  • HTML compression

Here is a basic production webpack.config.js configuration

const { resolve } = require('path') const MiniCssExtractorPlugin = require('mini-css-extract-plugin') const OptimiziCssAssetsWebpackPlugin = require('optimizi-css-assets-webpack-plugin') const HtmlWebpackPlugin = Require ('html-webpack-plugin') // define node.js environment variables, Process.envs.NODE_ENV = 'production' // Reuse loader's writing const commonCssLoader = [// Replace style - loader. The loader: extract the CSS into a single file and then through the link in the js load MiniCssExtractPlugin. Loader, / / CSS - loader: // After the CSS loader processes the CSS file, the style file is in the JS file // Problem: 1. The size of the js file is large. Use MiniCssExtractPlugin. Alternative style - loader loader 'CSS - loader, / * postcss - loader: CSS compatibility processing: postcss - > need to install: Postcss-loader postcss-preset-env PostCSS needs to load the specified CSS compatibility style using the configuration in browserslist in package.json. "Browserslist ": {// development environment --> Set node environment variable: process.env.node_env = development" development": [// "last 1 Chrome version", "last 1 Firefox version", "last 1 Safari version"], "Production ": [// must be compatible with most browsers ">0.2%", "not dead", "not op_mini all"]}, */ {loader: 'postcss-loader', options: {ident: 'postcss', // plugins: () => [// postCSS plug-in require(' postcsS-preset -env')(),],},},] module.exports = {entry: './src/js/index.js', output: { filename: 'js/built.js', path: resolve(__dirname, 'build'), }, module: { rules: [ { test: /\.css$/, use: [...commonCssLoader], }, { test: /\.less$/, use: [...commonCssLoader, 'less-loader'],}, /* Normally, a file can only be processed by one loader. }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} Airbnb (a popular JS style) --> you need to download eslint-config-Airbnb-base eslint-plugin-import to set the checking rules: "EslintConfig ": {"extends":" Airbnb-base ", // extends airbnb style specification "env": {"browser": }} */ test: /\.js$/, exclude: /node_modules/, // Ignore node_modules/, // enforce: Loader: 'eslint-loader', options: {// automatic fix: True,},}, /* js compatibility processing: need to download babel-loader @babel/core 1. 2. Full js compatibility handling --> @babel/ Polyfill problem: It is too large to solve a partial compatibility problem but to introduce all the compatibility code. 3. --> core-js */ {test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: {useBuiltIns: 'usage', // Load corejs on demand: {version: Targets: {// Specify which version of the browser is compatible with Chrome: '60', Firefox: '50', Ie: '9', Safari: '10', edge: '17'}},,,}}, {/ / picture processing test: / \. (JPG | PNG | GIF) /, the loader: 'url - loader' options: {limit: 8 * 1024, name: '[hash:10].[ext]', outputPath: 'imgs', esModule: false, // disable url-loader default es6 modular parsing},}, // HTML image processing {test: $/, / \. HTML loader: 'HTML - loader'}, / / handle other file {exclude: / \. (js | | CSS less | | HTML JPG | PNG | GIF) /, loader: 'file-loader', options: { outputPath: 'media', }, }, ], }, plugins: [new MiniCssExtractPlugin({// rename the output CSS file filename: 'the CSS/built. CSS'}), / / compress CSS new OptimiziCssAssetsWebpackPlugin (), / / HtmlWebpackPlugin: New HtmlWebpackPlugin({template: './ SRC /index.html', // compress the HTML code minify: {// remove the space collapseWhitespace: true, // remove the comment removeComments: }),], // The js code will be compressed automatically in production mode: 'production',}Copy the code

Webpack optimized configuration

5.1 Optimization of development environment performance

5.1.1 HMR (Hot Replacement of Modules)

HMR: Hot Module replacement Hot module replacement or hot module replacement

What it does: when a module changes, only one of the modules is repackaged and built (instead of all the modules), greatly speeding up the build process

Set hot to true in devServer to automatically enable HMR (only available in development mode)

devServer: { contentBase: resolve(__dirname, 'build'), compress: true, port: 3000, open: Hot: true} hot: true} Hot: true} Hot: true} Hot: true}Copy the code

Hot-module replacement for each file:

  • Style files: You can use the HMR function because the styleloader used in the development environment implements hot module replacement internally by default

  • Js file: HMR function cannot be used by default (if you modify a JS module, all js modules will be refreshed) –> To implement HMR, you need to modify js code (add HMR function code).

    // Bind if (module.hot) {// Once module.hot is true, the HMR function is enabled. Module.hot. accept('./print.js', function() {// the method listens for changes to the print.js file. If the changes happen, only this module will be repackaged and built. Print (); // Print (); }); }Copy the code

    Note: the HMR function can only process js files that are not the entry JS files.

  • HTML file: HMR is not allowed by default (HTML does not need HMR, because there is only one HTML file, no need to optimize) using HMR will cause problems: HTML files will not be hot updated (not automatically packaged build) Modify the entry to import the HTML file (so that the HTML changes as a whole refresh)

    entry: ['./src/js/index.js', './src/index.html']
    Copy the code

5.1.2 the source – the map

13. source-map: A technique that provides a mapping of source code to post-build code (if there is an error in the post-build code, the mapping can be used to track the source code error)

Parameters: [the inline – | hidden – | eval -] [nosources -] [being – [module -]] source – the map

Code:

devtool: 'eval-source-map'
Copy the code

Alternative: [to generate source – the map the location of the | the given error code information]

  • Source-map: External, error code accurate information and source code error location
  • Inline-source-map: Generates only an inline-source-map with the exact information about the error code and the location of the error in the source code
  • Hidd-source-map: external, error code error cause, but no error location (to hide the source code), source code error cannot be traced, only to the error location of the built code
  • Eval-source-map: Inline, each file generates the corresponding source-map. In eval, accurate information about the error code and error bits of the source code
  • Nosource-source-map: External, error code accurate information, but nosource information (to hide source)
  • Cheap-source-map: External, accurate information about the error code and the location of the error in the source code. Errors can only be accurate to the whole line, ignoring the columns
  • Cheap – modol-source-map: external, accurate error code information and source code error location, module will be added to the loader source-map

Differences between inline and external: 1. External generates files, inline does not 2. Inline builds are faster

Development/production environment options:

Development environment: Need to be fast and debug friendly

  • Fast (eval > inline > cheap >…)
    1. eval-cheap-souce-map
    2. eval-source-map
  • Debugging is friendlier
    1. souce-map
    2. cheap-module-souce-map
    3. cheap-souce-map

Eval-source-map (high integrity, fast inline)/eval-cheap-mode-souce-map (error message ignore column but contain other information, fast inline)

Production environment: Consider whether source code should be hidden and whether debugging should be more friendly

  • Inlining makes the code bulky, so don’t do it in production
  • Hide source code
    1. Nosource-source-map All hidden
    2. Hidd-source-map hides only source code and prompts you for code error messages after building

> source-map (most complete)/cheap-modole-souce-map (error: whole row ignored column)

5.2 Production Environment performance optimization

5.2.1 Optimize the packaging build speed

5.2.1.1 oneOf

OneOf: After the match is made to the Loader, the match is no longer carried out backwards, which optimizes the packaging build speed of the production environment

Code:

Module: {rules: [{// js syntax check test: /\.js$/, exclude: /node_modules/, // Enforce first: 'pre', loader: 'eslint-loader', options: { fix: True}}, {// oneOf optimize the build speed of the production environment // OneOf: [{test: /\.css$/, use: [...commonCssLoader]}, {test: /\.less$/, use: [...commonCssLoader, 'less-loader']}, {// js compatibility processing test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: { presets: [ [ '@babel/preset-env', { useBuiltIns: 'usage', corejs: {version: 3}, targets: { chrome: '60', firefox: '50' } } ] ] } }, { test: /\.(jpg|png|gif)/, loader: 'url-loader', options: { limit: 8 * 1024, name: '[hash:10].[ext]', outputPath: 'imgs', esModule: false } }, { test: /\.html$/, loader: 'html-loader' }, { exclude: /\.(js|css|less|html|jpg|png|gif)/, loader: 'file-loader', options: { outputPath: 'media' } } ] } ] },Copy the code

5.2.1.2 Babel cache

Babel cache: like HMR, it caches the resources that Babel has processed (where js changes are updated, other JS are still cached) to make the second wrapper build faster

Code:

{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: { presets: [ [ '@babel/preset-env', { useBuiltIns: 'usage', corejs: { version: 3 }, targets: { chrome: '60', firefox: '50'}}], // Enable the Babel cache // On the second build, read the previous cache cacheDirectory: true}},Copy the code

File resource cache

The file name is not changed, so the request is not rerequested, but the cached resource is used again

1. Hash: A unique hash value is generated each time wePack is packaged.

Problem: Repackaging changes the HSAH values of all files, invalidating all caches. (Maybe only one file was changed)

2. Chunkhash: Indicates the hash value generated based on the chunk. The hash values of the same chunk are the same

Problem: js and CSS are in the same chunk and have the same hash value (because csS-loader loads CSS files into JS, so they belong to the same chunk)

3. Contenthash: Generates a hash value based on the file content. Different files must have different hash values (the hash in the file name changes only when the file content is changed).

If you modify the content of the CSS file, the hash value of the packaged CSS file name will change, while the hash value of the js file will remain unchanged. In this way, the CSS and JS cache will determine whether to re-request the resource separately

5.2.1.3 Multi-process Packaging

Multi-process packaging: if a task takes a long time, it will lag. Multi-process can do more than one thing at a time, which is more efficient.

The advantage is increased packaging speed, but the disadvantage is that there is overhead to start and communicate each process (Babel-Loader takes the longest time, so use thread-loader to optimize for it).

{ test: /\.js$/, exclude: /node_modules/, use: [/* babel-Loader] [/* Babel-Loader] [/* Babel-Loader] [/* Babel-Loader] [/* Babel-Loader] [/* Babel-Loader] [/* Babel-Loader] */ {loader: 'thread-loader', options: {workers: 2 // processes 2}}, {loader: 'babel-loader', options: {presets: [ [ '@babel/preset-env', { useBuiltIns: 'usage', corejs: { version: 3 }, targets: { chrome: '60', firefox: '50'}}]], // Enable Babel cache // The second build will read the previous cache cacheDirectory: true}}]},Copy the code

5.2.1.4 externals

Externals: Let some libraries be introduced via CDN without packaging

Webpack.config.js

Externals: {// reject jQuery to be packaged (CDN is faster) // Ignore the library name -- NPM package name jQuery: 'jQuery'}Copy the code

Needs to be introduced in index.html via CDN:

< script SRC = "https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js" > < / script >Copy the code

5.2.1.5 DLL

DLL: Let certain libraries be packaged separately and then imported directly into the build. You can split node_modules after code split and then use the DLL to split more finely to optimize the performance of code running.

Webpack.dll.js configuration :(jquery is packaged separately)

/* Node_modules modules will be packaged together, but the output js file is too large for many libraries. When running WebPack, the default is to look for the webpack.config.js configuration file requirements: */ const {resolve} = require('path'); */ const {resolve} = require('path'); const webpack = require('webpack'); Exports = {module. Exports = {module. Exports = {module. Exports = {module. Jquery path: resolve(__dirname, 'DLL '); jquery path: resolve(__dirname,' DLL ') '[name]_[hash]', // what is the name of the package library that is exposed to the outside}, plugins: New webpack.dllplugin ({name: {name: {name: {name: {name: {name:}}) Path: resolve(__dirname, 'DLL /manifest. Json ')}), mode: 'production'};Copy the code

Webpack.config. js configuration :(tells webpack that you don’t need to package jquery, and output jquery to build directory along with other packaged resources)

// Introduce plugin const webpack = require('webpack'); const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin'); // plugins: [new HtmlWebpackPlugin({template: ' '. / SRC/index. HTML}), / / tell webpack which libraries don't participate in the packaging, use at the same time also have to change the name of the new webpack. DllReferencePlugin ({manifest: Resolve (__dirname, 'DLL /manifest.json')}), // package a file to the build directory. And in HTML automatically is introduced into the resources of new AddAssetHtmlWebpackPlugin ({filepath: resolve (__dirname, 'the DLL/jquery. Js)})],Copy the code

5.2.2 Optimize the performance of code running

5.2.2.1 cache

5.2.2.2 Tree shaking

Tree Shaking: Removing useless code

Prerequisites: 1. Must use ES6 modularity 2. Start production environment (this will automatically remove useless code)

What it does: Reduces code volume

In package.json, configure:

“SideEffects “: false means that all code has no sideEffects.

Problem with this: It may kill CSS / @babel/polyfill (side effect)

So you can configure: “sideEffects”: [“*.css”, “*.less”] does not handle CSS /less file tree shaking

5.2.2.3 Code Split

Code segmentation. Splitting a large bundle.js file that packs the output into smaller files allows you to load multiple files in parallel, which is faster than loading one file.

1. Multi-entry split

/ SRC /js/index.js', test: './ SRC /js/test.js'}, output: Filename: 'js/[name].[contenthash:10].js', path: resolve(__dirname, 'build')},Copy the code

2. Optimization:

optimization: {
    splitChunks: {
      chunks: 'all'
    }
  },
Copy the code
  • Package the code in node_modules separately (over 30KB in size)
  • Automatically analyze whether there are public files in the multi-entry chunk. – If any is packaged into a single chunk(for example, if jquery is introduced in both modules, it will be packaged into a single file) (size greater than 30KB)

3. Dynamic import syntax:

/* Use javascript code to package a file as a chunk import: WebpackChunkName: specifies the name of the test file packaged separately */ import(/* webpackChunkName: 'test' * '/ test'), then (({count} the mul,) = > {/ / file loading successful ~ / / eslint - disable - next - line console. The log (the mul (2, 5)); = > {}). The catch () / / eslint - disable - next - line console. The log (' file loading failure ~ '); });Copy the code

5.2.2.4 Lazy Loading (Lazy Loading/PreLoading)

1. Lazy loading: Loading files only when they are needed (requires code splitting). However, if the resource is large, the load time will be long and there will be delay.

2. Normal loading: You can think of it as parallel loading (loading multiple files at the same time). If you load unnecessary resources first, it will waste time.

3. Prefetch (poor compatibility) : Prefetch is loaded before use. Wait until all the other resources are loaded and the browser is idle, then secretly load this resource. It’s loaded when you use it, and it’s fast. So it’s better to add preloading to lazy loading.

Code:

Document.getelementbyid (' BTN ').onclick = function() {document.getelementById (' BTN ').onclick = function() { Import (/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) => { console.log(mul(4, 5)); }); import('./test').then(({ mul }) => { console.log(mul(2, 5)) }) };Copy the code

5.2.2.5 PWA (Offline Accessibility technology)

Pwa: Offline accessibility technology (progressive web development applications) using ServiceWorker and Workbox technologies. The advantage is that it can be accessed offline, but the disadvantage is poor compatibility.

Webpack.config.js

const WorkboxWebpackPlugin = require('workbox-webpack-plugin'); / / introduce plug-in / / plugins to add: new WorkboxWebpackPlugin. GenerateSW ({/ * 1. 2. Delete the old serviceWorker and generate a serviceWorker configuration file */ clientsClaim: true, skipWaiting: true})Copy the code

To activate its use, you need to write another code in index.js:

/* 1. Eslint does not know window and navigator global variables: you need to modify the package.json eslintConfig config "env": {"browser": 2. Sw code must run on server --> nodejs or --> NPM I serve -g serve -s build */ if ('serviceWorker' in Navigator) {// handle compatibility issues windox.addeventListener ('load', () => {navigator.serviceworker.register ('/service-worker.js') // Register serviceworker.then () => { Console. log('sw registration succeeded ~'); }). Catch (() => {console.log('sw registration failed ~'); }); }); }Copy the code