An overview of the
Webpack is a static module packaging tool for modern JavaScript applications. When Webpack processes an application, it internally builds a dependency diagram that maps to each module required for the project and generates one or more bundles
Dependencies and Installation
Webpack is dependent on the Node environment to run, so you need Node installed on your computer
When installing Webpack, install WebPack-CLI at the same time
Yarn add webpack webpack-cli -g # Global installation YARN add webpack webpack-cli -d # Local installationCopy the code
Initial project
Run yarn init -y to initialize the project and create the SRC /index.js file in the root directory
Yarn add webpack webpack-cli -d # Local installationCopy the code
// src/index.js
class Person{
constructor (name, age) {
this.name = name
this.age = age
}
sayHi () {
console.log(this.name)
}
}
const p = new Person('zs', 18)
p.sayHi()
Copy the code
Execute the webpack command for default packaging
webpack
Copy the code
A dist directory is automatically generated in the root directory, which contains a packaged JS file -main.js
The default package
When we run webpack, webpack looks for SRC /index.js in the current directory as an entry point and automatically generates dist/main.js
We also found that the code in the main.js file had been tampered with because the default packaging mode for WebPack is Production mode
Webpack.config.js configuration file
The default configuration must not meet the requirements of project packaging, we can create a webpack.config.js file in the root directory to configure webpack, and by default, please ensure that the file name is only webpack.config. Of course, the configuration file name can also be changed through other configurations
In the webpack.config.js file, we do an initial base configuration of WebPack. Webpack relies on Node, so we follow the CommonJs modular specification
Entrance and exit configuration
// webpack.config.js const {resolve} = require('path') module.exports = {// webpack.config.js const {resolve} = require('path') module. './ SRC /index.js', // export configuration, output: {// filename: Resolve path: resolve(__dirname, 'build')}}Copy the code
webpack
Copy the code
After executing the webpack command to pack, we will find that the build/bundle.js file is generated in the root directory, where we specify the import and export through the configuration file
Script execution
In the package.json file in the root directory, we can add scripts to pack fixed commands specified by Webpack
The "{" name" : "08," version ":" 1.0.0 ", "main" : "index. Js", "license" : "MIT", "devDependencies" : {" webpack ": "^ 5.14.0 webpack -", "cli" : "^ 4.3.1"}, "scripts" : {" build ":" webpack "}}Copy the code
Package using YARN Run Build
yarn run build
Copy the code
Specifying a configuration file
By default, the Webpack configuration file name can only be webpack.config. For some reason, it is sometimes necessary to use a different configuration file depending on the specific situation. We can configure the execution command to specify the Webpack configuration file
The "{" name" : "08," version ":" 1.0.0 ", "main" : "index. Js", "license" : "MIT", "devDependencies" : {" webpack ": "^ 5.14.0 webpack -", "cli" : "^ 4.3.1"}, "scripts" : {" build ":" webpack - config prod. Config. Js "}}Copy the code
Add –config prod.config.js to the webpack command, and webpack will be packaged with the prod.config.js file in the root directory as the configuration file
Style to deal with
Create a CSS file, SRC /style/common.css, and write some styles
// src/style/common.css
.container {
width: 300px;
height: 300px;
background-color: pink;
}
Copy the code
In the import file, we import CSS through the EsModule module import
// src/index.js
import './src/style/common.css'
Copy the code
Execute yanr run build to package. Console error, because Webpack can only understand JavaScript and JSON files, Webpack can not directly process CSS, need to use loader, You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file
loader
What is loader
Loader enables WebPack to process other types of files and convert them into valid modules for use by applications and to be added to dependency diagrams
CSS – loader and style – loader
- Css-loader loads the CSS file, parses the imported CSS file, and returns the CSS code
- Style-loader takes the exported content of the module as a style and adds it to the DOM
Install dependencies before use
yarn add css-loader style-loader -D
Copy the code
In the WebPack configuration file
const {resolve} = require('path') module.exports = { entry: './src/index.js', output: { filename: 'build.js', path: The resolve(__dirname, 'build')}, module: {// rules attribute is an array that configates information for different resources. Rules: [{// Is used to match resources, and is usually set to the regular expression test: / \. CSS $/, / / configure loader use: [/ / {loader: 'style - loader'} / / {loader: 'css-loader', 'css-loader']}]}}Copy the code
After the yarn Run build command is executed, create build/index.html and import the build.js file using the script tag. Then create a label named Container and open the browser to view the effect
Note that the loader order should be guaranteed: ‘style-loader’ first, and ‘CSS-loader’ last. If this convention is not followed, WebPack may throw an error
less-loader
In development, we usually use less, SASS and other preprocessors to write CSS. The CSS compiled by less and SASS needs to be converted to common CSS using tools. The following uses less as an example
Create a less file, SRC /style/common.less, and write some styles
@color: #666;
@font-weight: 700;
.title {
color: @color;
font-weight: @font-weight;
}
Copy the code
In the import file, we import CSS through the EsModule module import
// src/index.js
import './src/style/common.less'
Copy the code
First we need less tool to transform CSS. Less-loader automatically uses less tool
Install dependencies
yarn add less less-loader -D
Copy the code
In the WebPack configuration file
// webpack.config.js const {resolve} = require('path') module.exports = { entry: './src/index.js', output: { filename: 'build.js', path: resolve(__dirname, 'build') }, module: { rules: [ { test: /\.css$/i, use: ['style-loader', 'css-loader']}, {// match less file resources test: /\. Less $/ I, use: [// Convert from less to CSS, csS-loader loads CSS, style-loader inserts CSS into Dom {loader: 'style-loader'}, {loader: 'css-loader'}, {loader: 'less-loader'} ] } ] } }Copy the code
PostCSS
Postcss is a tool for converting CSS code with JavaScript tools and plug-ins
When installing PostCSS, install PostCSS-CLI at the same time
yarn add postcss postcss-cli -D
Copy the code
autoprefixer
The Autoprefixer plugin automatically captures the browser’s popularity and supported attributes, and automatically prefixes CSS rules based on this data
yarn add autoprefixer -D
Copy the code
We can add some properties to the CSS file
// src/style/common.css
:fullscreen {
}
.test {
user-select: none
}
Copy the code
Use the Postcss tool from the command line, and specify autoprefixer
npx postcss --use autoprefixer -o ./src/style/commoncopy.css ./src/style/common.css
Copy the code
After executing this command, we can see that there is a commoncopy. CSS file in the SRC /style directory, and the CSS properties are automatically prefixed
// src/style/commoncopy.css
:-webkit-full-screen {
}
:-ms-fullscreen {
}
:fullscreen {
}
.test {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none
}
Copy the code
postcss-loader
In the development, we will not directly use the command tool to process CSS, we can use the PostCSS tool with Webpack, can be done by postCSS-loader
yarn add postcss-loader -D
Copy the code
In the WebPack configuration file
const {resolve} = require('path') module.exports = { entry: './src/index.js', output: { filename: 'build.js', path: resolve(__dirname, 'build') }, module: { rules: [ { test: /.css$/i, use: [ 'style-loader', 'css-loader', { loader: 'postcss-loader', options: {postcssOptions: {postcssloader: {postcssloader: {postcssloader: {postcssloader: {postcssloader: {postcssloader: {postcssloader: { [ require('autoprefixer') ] } } } ] }, ] } }Copy the code
postcss-preset-env
The postCSS-Preset -env plugin helps us turn some of the modern CSS features into CSS that most browsers know, and adds the needed polyfills based on the target browser or runtime environment, and automatically helps us to add autoprefixer
yarn add postcss-preset-env -D
Copy the code
In the WebPack configuration file
const {resolve} = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'build.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /.css$/i,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [ require('postcss-preset-env') ]
}
}
}
]
},
]
}
}
Copy the code
Browserslist
Features supported by different browsers: CSS features, JS syntax, compatibility between browsers, whether all browsers in the market need to be compatible
Browserslist is a configuration that shares the target browser and node.js version between different front-end tools
Browserslist writes rules
In the package. The json configuration
{" devDependencies ": {" autoprefixer" : "^ 10.3.1", "CSS - loader" : "^ 6.2.0", "less" : "^ 4.4.1", "less - loader" : "^ 10.0.1 postcss", ""," ^ 8.3.6 ", "postcss - cli" : "^ 8.3.1", "postcss - loader" : "^ 6.1.1", "postcss - preset - env" : "^ 6.7.0 style -", "loader" : "^ 3.2.1", "webpack" : "^ 5.14.0", "webpack - cli" : "^ 4.3.1"}, "browserslist" : [ "> 1%", "last 2 versions", "no dead" ] }Copy the code
.browserslist file configuration
In addition to configuration in package.json, you can also create a.browserslist file in the root directory for configuration
> 1%
last 2 versions
no dead
Copy the code
Browserslist data comes from Can I Use, and our previous autoprefixer and PostCSS-Preset -env, as well as other plug-in tools, are compatible with these configurations
If we don’t configure Browserslist, browserslist will have a default configuration
Working with other resources
In the development process, sometimes we rely on other resources, such as images, fonts, videos, etc., and use corresponding Loaders to process these resources
file-loader
yarn add file-loader -D
Copy the code
File-loader will parse the import/require() on the file into a URL and send the file to the output directory
Import the pre-prepared image into the entry file
// src/index.js
const img = new Image()
img.src = require('./images/1.png).default
document.body.appendChildren(img)
Copy the code
In the WebPack configuration file
const {resolve} = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)$/i,
use: [
{
loader: 'file-loader',
},
],
}
]
}
}
Copy the code
After yarn Run build is executed, we can see that there is an additional image in the build folder, and the name is similar to the hash value. We create an index. HTML file and import the bundle.js file with the script tag
Setting the file name
It would work with a document’s original name or placeholder extension
File naming rules
The commonly used placeholder
- [ext] handles the extension of the file
- [name] Specifies the name of the processing file
- The contents of the [hash] file, processed using the hash function of MD4
- [hash:
] Specifies the length of the computed hash
- [path] The path of the file relative to the WebPack configuration file
In the WebPack configuration file
const {resolve} = require('path') module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: resolve(__dirname, 'build') }, module: { rules: [ { test: /\.(png|jpe?g|gif|svg)$/i, use: [ { loader: 'file-loader', options: {name: '[name].[hash:8].[ext]', // Use outputPath to specify the folder to save the file. 'img / / or in the following way, the specified file after packaging storage folder at the same time, set the name / / name:' img / [name] [8] hash: [ext] '}},]],}}}Copy the code
url-loader
yarn add url-loader -D
Copy the code
Url-loader is similar to file-loader. It converts a file to a Base64 URI and returns a DataURL if the file is smaller than the byte limit
Import the pre-prepared image into the entry file
// src/index.js
const img = new Image()
img.src = require('./images/1.png)
document.body.appendChildren(img)
Copy the code
In the WebPack configuration file
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)$/i,
use: [
{
// loader: 'file-loader',
loader: 'url-loader',
options: {
name: '[name].[hash:8].[ext]',
outputPath: 'img'
// name: 'img/[name].[hash:8].[ext]'
}
},
],
}
]
}
}
Copy the code
After removing the previous build file and executing yarn Run Build, we can see that there are no images in the build folder. Create an index. HTML file and import the bundle.js file with the script tag. Images can still be viewed through a browser, because by default urL-loader converts all image files to Base64 encoding
Url – limit of loader
After developing base64, the miniaturized transformation can be requested along with the page, reducing the unnecessary request process. Large images can be used directly, if large images are also converted, but will affect the speed of the page request
Limit Allows you to set the conversion limit
const {resolve} = require('path') module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: resolve(__dirname, 'build') }, module: { rules: [ { test: /\.(png|jpe?g|gif|svg)$/i, use: [ { // loader: 'file-loader', loader: 'url-loader', options: { name: '[name].[hash:8].[ext]', outputPath: "Img", / / limit in bytes limit: 1024}},]],}}}Copy the code
Asset Modules Type
In Webpack5, asset Modules Type allows you to load other resources (fonts, ICONS, etc.) without having to configure additional loaders such as file-loader, url-loader, raw-loader.
Asset Modules Type replaces all of these Loaders by adding four new module types
asset/resource
Emit a separate file and export the URL. It used to be possible to usefile-loader
.asset/inline
Export the data URI of the resource. It used to be possible to useurl-loader
.asset/source
Export the source code of the resource. It used to be possible to useraw-loader
.asset
Automatically choose between exporting the data URI or issuing a separate file. It used to be possible to useurl-loader
, and configure volume size limits to achieve this.
Import an image
// src/index.js
const img = new Image()
img.src = require('./images/1.png')
document.body.appendChild(img)
Copy the code
Previously we need to configure file-loader for processing, webpack5 can directly set type to process resources
const {resolve} = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: 'asset/resource'
}
]
}
}
Copy the code
Customize the output path and file name of the file
- Modify Output and add the assetModuleFilename property
const {resolve} = require('path') module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: Resolve (__dirName, 'build'), // Add assetModuleFilename attribute assetModuleFilename: 'img/[name].[Hash :8]. { rules: [ { test: /\.(png|jpe?g|gif|svg)$/i, type: 'asset/resource' } ] } }Copy the code
- Add a Generator property and set filename
const {resolve} = require('path') module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: Resolve (__dirName, 'build'), // Add assetModuleFilename attribute // assetModuleFilename: 'img/[name].[hash:8].[ext]'}, module: {rules: [{test: / \. (PNG | jpe? G | | GIF SVG) $/ I type: 'asset/resource', / / add the generator, set the filename attribute the generator: { filename: 'img/[name].[hash:8].[ext]' } } ] } }Copy the code
Volume conversion limit
The volume conversion was previously restricted by limit in urL-loader
- Set type to asset
- Add the Parser attribute, specify the condition for the dataUrl, and add the maxSize attribute
const {resolve} = require('path') module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: resolve(__dirname, 'build'), }, module: { rules: [ { test: /\.(png|jpe?g|gif|svg)$/i, // type: 'asset/resource', type: 'asset', generator: {filename: 'img/[name].[hash:8].[ext]'}, Parser: {dataUrlCondition: {// Set maxSize: 100000}}}]}}Copy the code
The font is loaded
When using a particular font or font icon, some font files are introduced, and these font files are handled in much the same way
// src/font/font.css @font-face { font-family: "calculatrix-7"; src: url(.. /font/calculatrix-7.ttf); } @font-face { font-family: "pang-men-zheng-dao"; src: url(.. /font/pang-men-zheng-dao.ttf); } @font-face { font-family: "you-she-biao-ti-hei"; src: url(.. /font/you-she-biao-ti-hei.ttf); }Copy the code
// src/style/index.css
.title {
font-family: 'calculatrix-7';
}
.title2 {
font-family: 'pang-men-zheng-dao';
}
.title3 {
font-family: 'you-she-biao-ti-hei';
}
Copy the code
Import styles and add Dom elements and set class names
// src/index.js
import './font/font.css'
import './style/index.css'
const createSpan = (className) => {
const el = document.createElement('p')
el.classList.add(className)
el.innerText = 'hello webpack'
return el
}
document.body.appendChild(createSpan('title1'))
document.body.appendChild(createSpan('title2'))
document.body.appendChild(createSpan('title3'))
Copy the code
Webpack configuration file
const {resolve} = require('path') module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: resolve(__dirname, 'build'), }, module: { rules: [ { test: /\.css$/i, use: [' style - loader ', 'CSS - loader]}, / / configuration rules font package {test: / \. (woff2? | eot | the vera.ttf) $/, type:' asset/resource, the generator: { filename: 'font/[name].[hash:8].[ext]' } } ] } }Copy the code
Plugins
Plugins are the backbone of Webpack. Webpack itself is built on top of the same plugin system you use in your Webpack configuration!
Plugins can also do things that loaders cannot do. Plugins can be used to perform a wider range of tasks, such as packaging optimization, resource management, environment variable injection, and so on
CleanWebpackPlugin
After each configuration change, we need to package again, because each package may generate different files, some original files will be retained, so we need to manually delete the last package folder
The CleanWebpackPlugin plugin helps you do this automatically
yarn add clean-webpack-plugin -D
Copy the code
Webpack configuration file
Const {resolve} = require('path') // import CleanWebpackPlugin module.exports = { entry: './src/index.js', output: { filename: 'bundel.js', path: Resolve (__dirname, 'build')}, // Use plugins in plugins: [new CleanWebpackPlugin()]}Copy the code
This way the CleanWebpackPlugin will automatically delete the last package for us each time we pack
HtmlWebpackPlugin
After each package, we need to manually create an index. HTML file, and also need to manually import bundle.js, which is quite troublesome. For HTML packaging, we can use HtmlWebpackPlugin
yarn add html-webpack-plugin -D
Copy the code
Webpack configuration file
Const {resolve} = require('path') // import CleanWebpackPlugin // import HtmlWebpackPlugin const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = {entry: './ SRC /index.js', output: {filename: 'bundel.js', path: resolve(__dirname, 'build')}, [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'hello webpack' }) ] }Copy the code
When we pack again, we’ll find an index. HTML file in the build folder and automatically import bundle.js. The content in
Customize HTML templates
Sometimes the default generated HTML template content is not what we want. In this case, we can create our own template HTML file by customizing the template. Here we use the index.html template file of the Vue project as an example
Root record create public folder and create index.html
<! -- public/index.html --> <! -- Vue index.html template file --> <! DOCTYPE html> <html lang=""> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta Name ="viewport" content="width=device-width,initial-scale=1.0"> <link rel="icon" href="<%= BASE_URL %> <title><%= htmlWebpackPlugin.options.title %></title> </head> <body> <noscript> <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> </noscript> <div id="app"></div> <! -- built files will be auto injected --> </body> </html>Copy the code
Webpack configuration file
Const {resolve} = require('path') // import CleanWebpackPlugin // import HtmlWebpackPlugin const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = {entry: './src/index.js', output: { filename: 'bundel.js', path: resolve(__dirname, 'build') }, plugins: [new CleanWebpackPlugin(), new HtmlWebpackPlugin({title: 'hello webpack', // specify the location of the HTML template file: './public/index.html' }) ] }Copy the code
At this point, we will find that the console reported an error
ERROR in Template execution failed: ReferenceError: BASE_URL is not defined
ERROR in ReferenceError: BASE_URL is not defined
The reason is that in our template file DefinePlugin
An error occurred while compiling and packaging the template HTML file. The reason is that we used a BASE_URL constant in the template file, but we did not define this constant value, so there was an undefined error
DefinePlugin allows configured global constants to be created at compile time and does not need to be installed separately because it is a built-in plug-in for WebPack
Webpack configuration file
const {resolve} = require('path') const {CleanWebpackPlugin} = require('clean-webpack-plugin') const HtmlWebpackPlugin = // import DefinePlugin const {DefinePlugin} = require('webpack') module.exports = {entry: './src/index.js', output: { filename: 'bundel.js', path: resolve(__dirname, 'build') }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'hello webpack', template: ' '. / public/index. HTML}), new DefinePlugin ({/ / configuration BASE_URL constants BASE_URL: '". / "})]}Copy the code
CopyWebpackPlugin
yarn add copy-webpack-plugin -D
Copy the code
In the process of packaging, sometimes we need to copy some files into the build folder, such as the favicon.ico icon
Ico icon file in the public directory. We want to have Favicon. ico in the packaged build folder as well. CopyWebpackPlugin can copy existing individual files or entire directories to the build directory
Webpack configuration file
const {resolve} = require('path') const {CleanWebpackPlugin} = require('clean-webpack-plugin') const HtmlWebpackPlugin = // import CopyWebpackPlugin const {DefinePlugin} = require('webpack') // import CopyWebpackPlugin const CopyWebpackPlugin = require('copy-webpack-plugin') module.exports = { entry: './src/index.js', output: { filename: 'bundel.js', path: resolve(__dirname, 'build') }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'hello webpack', // specify the location of the HTML template file template: './public/index.html'}), new DefinePlugin({BASE_URL: New CopyWebpackPlugin({patterns: [{// set copy source from: 'public', // allows configuration of the glob pattern matching library used by the plugin globOptions: {// Supports files to be excluded from the options list. Ignore: [//.ds_store MAC directory automatically generated file, do not need to copy '**/.ds_store ', // index.html template file, do not need to copy '**/index.html']}}]})]}}Copy the code
Mode
The Mode configuration option tells WebPack to use its built-in optimizations accordingly
Production mode is used by default
Configurable mode are: ‘none’ | ‘development’ | ‘production’
options | describe |
---|---|
development | Set up theDefinePlugin In theprocess.env.NODE_ENV 为 development To enable valid names for modules and chunks |
production | Set up theDefinePlugin In theprocess.env.NODE_ENV 为 production To enable deterministic obfuscated names for modules and chunks,FlagDependencyUsagePlugin .FlagIncludedChunksPlugin .ModuleConcatenationPlugin .NoEmitOnErrorsPlugin 和 TerserPlugin |
none | Opt out of any default optimization options |
Webpack configuration file
Const {resolve} = require('path') module.exports = {// config mode: 'development', // config mode: 'production', // Default packaging mode is production // mode: 'none', entry: './ SRC /index.js', output: {filename: 'bundle.js', path: resolve(__dirname, 'build') } }Copy the code
It also supports specifying the packaging mode on the command line
webpack --mode=development
Copy the code
Source Map
In daily development, source code and webpack compressed construction code is not the same, for example, in the production environment to write the source code error and the corresponding line after compilation must be inconsistent, at this time is very inconvenient debugging
The Source Map is explained in the MDN documentation to enable the browser to refactor the Source and render the refactor Source in the debugger
To borrow teacher Ruan Yifeng’s explanation, the Source map is an information file, which stores location information. That is, every position in the transformed code corresponds to the position before the transformation. With this, when something goes wrong, the debugger will display the original code instead of the converted code. This undoubtedly brings great convenience to developers
In Webpack, devtool controls whether and how Source maps are generated
Devtool has a lot of values to work with, different values generate different Source maps, and the build speed can vary depending on which values you choose
Production
In production, devtool is default and does not generate Source maps
Development
In the development environment, devtool defaults to eval and does not generate a Source Map
Also, if we set devtool to false, the Source Map will not be generated
eval
Eval mode, which converts lines of code into a string, is passed to the eval function with a comment appended to the end //# sourceURL
// SRC /index.js // Print a non-existent variable console.log(ABC)Copy the code
// webpack.config.js const {resolve} = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') const {CleanWebpackPlugin} = require(' cleanwebpack-plugin ') module.exports = {devtool defaults to eval mode: 'development', entry: './src/index.js', output: { filename: 'bundle.js', path: resolve(__dirname, 'build') }, plugins: [ new HtmlWebpackPlugin(), new CleanWebpackPlugin() ] }Copy the code
Although Eval does not generate the Source Map, eval performs the comments appended to the code to restore the corresponding file
source-map
When devtoop is source-map, a separate Source map file is generated
//# sourceMappingURL=bundle.js.map is appended to the end of the bundle.js build file with a comment pointing to the Source map file
// webpack.congif.js module.exports = {mode: 'development', // set source-map devtool: 'source-map' // set webpack.congif.js module.exports = {mode: 'development', // set source-map devtool: 'source-map'... }Copy the code
The browser will find the source-map file based on this comment
eval-source-map
When devtoop is eval-source-map, the source map is appended to the eval function with DataUrl
inline-source-map
When devtoop is inline-source-map, the source map is converted to the DataUrl and added to the bundle
The differences in the Source Map generated for different values can be seen in the official example, which is not illustrated here
recommended
Of the many devtoop values officially available, some are suitable for development environments, some are suitable for production environments, and quick Source Maps are often required for development
Development environment: Source-map or cheap-module-source-map is recommended
Production environment: default devtool options, source-map, hidden-source-map, nosource-source-map
Babel
Development typically involves using ES5+ ‘s advanced syntax or TypeScript and writing JSX, both of which are conversions to Babel, a toolchain, It is primarily used to convert code written in ECMAScript 2015+ syntax into backward-compatible JavaScript syntax so that it can run in current and older versions of browsers or other environments
The main features include syntax conversion, source conversion, Polyfill to add missing features to the target environment, and so on
use
Using Babel alone from the command line requires downloaded dependencies
yarn add @babel/core @babel/cli @babel/preset-env -D
Copy the code
// src/test.js
const sum = (a, b) => a + b
console.log(sum(1, 1))
Copy the code
The command line compiles all code from the SRC directory to the lib directory
/node_modules/. Bin/Babel SRC -- out-of-dir lib --presets=@babel/ env - # or NPX Babel SRC -- out-of-dir lib --presets=@babel/preset-envCopy the code
// lib/test.js
"use strict";
var sum = function sum(a, b) {
return a + b;
};
console.log(sum(1, 1));
Copy the code
At this point we have converted the ES2015+ syntax
In the dependencies we downloaded, the core functions of Babel are contained in the @babel/core module, @babel/ CLI is a tool that can be used from a terminal (command line), and @babel/preset-env is an intelligent preset for Babel
babel-loader
Babel-loader allows JavaScript files to be translated using Babel and Webpack
yarn add babel-loader -D
Copy the code
// src/index.js
console.log([1, 2, 3, 4].map(n => n * 2))
Copy the code
// webpack-config.js const {resolve} = require('path') module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: resolve(__dirname, 'build') }, module: { rules: [ { test: /\.m?js$/, use: {// Use babel-loader loader: 'babel-loader', options: {// Pass Babel presets: ['@babel/preset-env']}}}]}}Copy the code
/ / build/bundle. Js console. The log ([1, 2, 3, 4]. The map ((function (n)} {return 2 * n)));Copy the code
With babel-Loader, we also converted the ES2015+ syntax
By default, @babel/preset-env uses.browserslist as the configuration source. Browserslist was mentioned earlier in postCSS
If browserslist is not used as the configuration source, you can also use Targets to set the target browser
const {resolve} = require('path') module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: Resolve (__dirname, 'build')}, module: {rules: [{test: /\.m? Js $/, use: { 'babel-loader', options: {// Preset to Babel // presets: ['@babel/preset-env'] presets: [[' @ Babel/preset - env, {/ / set the targets the targets: [" chrome 58 ", "11" ie]}]]]}}}}}Copy the code
The configuration file
The Configuration File Types of Babel can be seen in the official Configuration File Types (babel.config with the js or JSON suffix as an example)
Create babel.config.js or babel.config.json in the root directory
// babel.config.js
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: [
"chrome 58",
"ie 11"
]
}
]
]
}
Copy the code
// babel.config.json
{
"presets": [
[
"@babel/preset-env",
{
"targets": [
"chrome 88",
"ie 11"
]
}
]
]
}
Copy the code
At this point we do not need to configure presets in the WebPack configuration file
// webpack.config.js module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: Resolve (__dirname, 'build')}, module: {rules: [{test: /\.m? Js $/, use: { 'babel-loader', } } ] } }Copy the code
Babel-polyfill, core-js, and Regenerator-Runtime
By default, Babel only converts new JavaScript syntax, not new apis, such as Iterator, Generator, Set, Maps, Proxy, Reflect, Symbol, Promise, etc. Before Babel 7.4.0, @babel/polyfill was used for conversion. Since Babel 7.4.0, this package has been deprecated. Instead, include core-js/stable(to populate ECMAScript features) and Regenerator-Runtime/Runtime (transpiled generator function required) directly to accomplish polyfill
yarn add core-js regenerator-runtime
Copy the code
Set the useBuiltIns
In the babel.config.js file in the root directory, we configure useBuiltIns
UseBuiltIns properties
-
Usage: Polyfill is used locally without global pollution
-
entry: Introduce core-js and Regenerator-Runtime via require or import. Introducing them more than once will give you an error, such as if one of the libraries we rely on itself uses some of polyfill’s features. All polyfills are imported based on the browserslist target browser, and the corresponding packaging file gets bigger
-
False: default value, do not use polyfill
Exports = {presets: [['@babel/preset-env', {// set useBuiltIns to usage // useBuiltIns: 'usage', // set useBuiltIns to entry useBuiltIns: 'entry'}]]}Copy the code
corejs
Corejs: A modular standard library for JavaScript. Includes ECMAScript to 2021 polyfills: Promises, Symbols, Collections, Iterators, typed Arrays, many other features, ECMAScript proposals, some cross-platform WHATWG/W3C features and proposals
Exports = {presets: [['@babel/preset-env', {// set useBuiltIns to usage // useBuiltIns: 'Usage ', // set useBuiltIns to entry useBuiltIns:' Entry ', // set corejs corejs: 3.17}]]}Copy the code
Exclude files that do not require polyfill, such as node_modules
const {resolve} = require('path') module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: Resolve (__dirname, 'build')}, module: {rules: [{test: /\.m? Js $/, // exclude: /node_modules/, use: Babel-loader loader: 'babel-loader'}}]}}Copy the code
Convert the following code with different configurations
// src/index.js
new Promise((resolve,reject) => {})
Copy the code
usage
Set useBuiltIns to Usage
// babel.config.js module.exports = {presets: [['@babel/preset-env', {// Set useBuiltIns to usage useBuiltIns: 'usage', // set corejs version corejs: 3.8}]]}Copy the code
Bundled bundel.js, I did a rough count and it’s about… I don’t know how many lines of code there are
Entry Set useBuiltIns to Entry
Core-js /stable and regenerator-Runtime/Runtime need to be introduced in the entry file
// SRC /index.js // introduce core-js/stable and regenerator-Runtime /runtime import 'core-js/stable' import 'regenerator-runtime/runtime' new Promise((resolve,reject) => {})Copy the code
Exports = {presets: [['@babel/preset-env', {// set useBuiltIns to usage // useBuiltIns: 'Usage ', // set useBuiltIns to entry useBuiltIns:' Entry ', // set corejs corejs: 3.17}]]}Copy the code
The bundel.js file is significantly larger than the Usage package
babel/preset-react
Convert JSX through Babel
yarn add @babel/preset-react -D
Copy the code
Webpack configuration file
// webpack.config.js
const {resolve} = require('path')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/index.jsx',
output: {
filename: 'bundle.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
}
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'hello react',
template: './index.html'
})
]
}
Copy the code
Entry JSX file
// src/index.jsx
import React, {Component} from 'react'
import ReactDom from 'react-dom'
class App extends Component {
constructor (props) {
super(props)
this.state = {
message: 'hello react'
}
}
render () {
return (
<div>
<h1>{this.state.message}</h1>
</div>
)
}
}
ReactDom.render(<App />, document.querySelector('#app'))
Copy the code
Index.html template file
<! -- index.html --> <! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, Initial - scale = 1.0 "> < title > Document < / title > < / head > < body > < div id =" app "> < / div > < / body > < / HTML >Copy the code
Babel configuration file
// babel.config.js module.exports = { presets: [ [ '@babel/preset-env', { useBuiltIns: 'entry', corejs: 3.17}], // set @babel/preset-react ['@babel/preset-react']]}Copy the code
Compile the TypeScript
Typescript can be converted to Javascript using either TS-Loader or Babel-loader
ts-loader
Ts-loader is converted to JavaScript through TypeScript’s compiler
Download the dependent
yarn add typescript ts-loader -D
Copy the code
Generate the tsconfig.json file
tsc --init
Copy the code
Configuration information
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: 'ts-loader'
}
]
}
Copy the code
babel-loader
yarn add @babel/preset-typescript -D
Copy the code
Using this default, Babel can convert Typescript to Javascript
// babel.config.js
module.exports = {
presets: [
// ...
['@babel/preset-typescript']
]
}
Copy the code
Eslint use
Install ESLint
yarn add eslit -D
Copy the code
Generating a Configuration File
npx eslint --init
Copy the code
The current configuration I generated is Airbnb-style Elsint. You can refer to the official website for detailed configuration
// .eslintrc.js
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'airbnb-base',
],
parserOptions: {
ecmaVersion: 13,
},
rules: {
},
};
Copy the code
EslintWebpackPlugin This plugin is used to find and fix problems in JavaScript code
yarn add eslint-webpack-plugin -D
Copy the code
Configuration information
const {resolve} = require('path')
const ESLintPlugin = require('eslint-webpack-plugin')
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: resolve(__dirname, 'build')
},
plugins: [
new ESLintPlugin()
]
}
Copy the code
DevServer
Webpack-dev-server can be understood as a small static file server that provides Web services for resource files generated by WebPack packaging
yarn add webpack-dev-server -D
Copy the code
Define package.json script to execute Webpack serve
{" name ":" serve ", "version" : "1.0.0", "main" : "index. Js", "license" : "MIT", "scripts" : {" build ":" webpack ", "serve" : "webpack serve" } }Copy the code
The mode is changed to development, using HtmlWebpackPlugin and providing templates to create HTML files
// webpack.config.js
const {resolve} = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: resolve(__dirname, 'build')
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html'
}),
new CleanWebpackPlugin()
]
}
Copy the code
Write a piece of code in the entry file
// src/index.js
class Persion {
constructor(name) {
this.name = name
}
sayHi() {
console.log(`Hello My Name is ${this.name}`)
}
}
const James = new Persion('James')
James.sayHi()
Copy the code
On the CLI, run the yarn serve script and access the browser using the address prompted by webpack-dev-server
We can see that the browser outputs what we want, and when we modify the code, we save it and the browser refreshes
HMR
Hot Module replacement (or HMR) is one of the most useful features webPack provides. It allows all types of modules to be updated at run time without a complete refresh.
Configure devServer to enable HMR
const {resolve} = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') const {CleanWebpackPlugin} = require('clean-webpack-plugin') module.exports = { mode: 'development', entry: // SRC /index.js', // configure devServer devServer: {// enable hot update hot: true}, output: {filename: 'bundle.js', path: resolve(__dirname, 'build') }, plugins: [ new HtmlWebpackPlugin({ template: './public/index.html' }), new CleanWebpackPlugin() ] }Copy the code
Specify which modules use HMR. When the specified module is updated, the status of the remaining modules is preserved to improve development efficiency
import './test' class Persion { constructor(name) { this.name = name } sayHi() { console.log(`Hello My Name is ${this.name} ')}} const James = new Persion('James') james.sayhi () if (module.hot) {// specify module Module. Hot. Accept ('. / test. Js', () = > {the console. The log (' test module updated ')})}Copy the code
In real development, there are so many module files that it is impossible to specify one module by one. For example, VUE-Loader supports THE HMR of VUE components to provide out-of-the-box experience
host
Specify the host address to use
The default value is localhost
If you want your server to be externally accessible, specify it like this
module.exports = { //... DevServer: {host: '0.0.0.0'}}Copy the code
port
Specify the port number to listen on requests:
module.exports = {
//...
devServer: {
port: 9527
}
}
Copy the code
Gzip compression
module.exports = {
//...
devServer: {
compress: true
}
}
Copy the code
Compressed from 294KB to 70.9KB
proxy
Proxying certain urls can be useful when you have separate API back-end development servers and want to send API requests on the same domain
module.exports = {
//...
devServer: {
proxy: {
'/api': 'http://localhost:3000'
}
}
}
Copy the code
Now, on/API/users request will request broker to http://localhost:3000/api/users. If you don’t want to pass/API, you need to rewrite the path:
module.exports = {
//...
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000',
pathRewrite: { '^/api': '' }
}
}
}
}
Copy the code
More information about proxy configuration can be found on the official website
resolve
How is the configuration module resolved
alias
Create an alias for import or require to make module introduction easier. For example, some common modules are located under the SRC/folder
// webpack.config.js
module.exports = {
// ...
resolve: {
alias: {
'@': resolve(__dirname, 'src')
}
}
}
Copy the code
This approach is handy when the module level is deep
// src/index.js
// import {sum} from './util.js'
import {sum} from '@/util.js'
console.log(sum(1, 1))
Copy the code
extensions
The ability to introduce modules without extensions and resolve these suffixes in order. If there are multiple files with the same name but different suffixes, WebPack will parse the files with the suffixes listed at the top of the array and skip the rest
// webpack.config.js
module.exports = {
// ...
resolve: {
alias: {
'@': resolve(__dirname, 'src')
},
extensions: ['.js', '.vue', '.json']
}
}
Copy the code
// src/index.js
// import {sum} from './util.js'
import {sum} from '@/util'
console.log(sum(1, 1))
Copy the code
The configuration file is removed. Procedure
. Currently we are through a webpack config. The configurations of js file, when the configuration information is more and more, this configuration file will be difficult to maintain, we should be divided according to the current environment configuration, such as the development environment and production environment can be differentiate different configuration, and do not need to use some configuration production environment, Some configurations are not required by the development environment, and some configurations are used by both the development and production environment. We need to make a common separation but separate configurations for the current environment
Creating a Configuration File
Create a config folder under the project root
Create three configuration files under this folder
webpack.base.conf
— Common infrastructure configuration for development and production environmentswebpack.dev.conf
— Development environment configurationwebpack.prod.conf
– Production environment configuration
Configuring the Execution Script
In the chapter on Specifying configuration files, I described how to configure a file with the –config command so that different files can be executed for different commands
Configure the execution script in package.json
Build — Project packaging
Serve — Start local service with DevServe
{
"scripts": {
"build": "webpack --config config/webpack.prod.conf",
"serve": "webpack serve --config config/webpack.dev.conf"
}
}
Copy the code
webpack-merget
Webpack-merge merges the common base configuration with the configuration partitioned by the current environment
yarn add webpack-merge -D
Copy the code
Common Base configuration
/*
* @Description base
*/
const {join} = require('path')
const resolve = (dir) => {
return join(__dirname, '..', dir)
}
module.exports = (env) => {
return {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: resolve('build')
}
// ...
}
}
Copy the code
The development environment
/*
* @Description dev
*/
const {merge} = require('webpack-merge')
const baseConfig = require('./webpack.base.conf')
module.exports = merge(baseConfig, {
mode: 'development',
//...
})
Copy the code
The production environment
/*
* @Description prod
*/
const {merge} = require('webpack-merge')
const baseConfig = require('./webpack.base.conf')
module.exports = merge(baseConfig, {
mode: 'production',
//...
})
Copy the code
The separation
Code separation is one of the most compelling features of WebPack. This feature enables you to separate code into different bundles and load these files on demand or in parallel. Code separation can be used to obtain smaller bundles and control resource load priorities, which, when used properly, can greatly affect load times.
- Entry starting point: Manually detach code using the Entry configuration.
- To prevent duplication, use Entry Dependencies or SplitChunksPlugin to delete and separate chunks.
- Dynamic import: Separation of code through inline function calls to modules.
Entry Point
Manually separating code through the Entry entry is the easiest and most intuitive way to separate code. However, this method of manual configuration is more, and there are some pitfalls
// webpack.config.js const {resolve} = require('path') module.exports = {mode: 'production', {index: './ SRC /index.js', main: './ SRC /main.js'}, output: {// name is the index of the entry object and main filename: '[name].bundle.js', path: resolve(__dirname, 'build') } }Copy the code
To prevent the repeat
We introduced the same third-party library in multiple modules at the same time, which resulted in repeated references in the packaged bundles
Entry dependencies
Configure the dependOn option option so that modules can be shared between multiple chunks
Here’s lodash as an example
// src/index.js
import _ from 'lodash'
console.log(_.join(['index', 'loaded!']))
Copy the code
// src/main.js
import _ from 'lodash'
console.log(_.join(['main', 'loaded!']))
Copy the code
// webpack.config.js
const {resolve} = require('path')
module.exports = {
mode: 'production',
entry: {
index: {import: './src/index.js', dependOn: 'shared'},
main: {import: './src/main.js', dependOn: 'shared'},
shared: ['lodash']
},
output: {
filename: '[name].bundle.js',
path: resolve(__dirname, 'build')
}
}
Copy the code
SplitChunksPlugin
The SplitChunksPlugin plug-in can extract a common dependency module into an existing chunk of entry or into a newly generated chunk. Using this plug-in, you can also remove duplicate LoDash modules. Webpack is installed and integrated by default and does not need to be installed separately
The splitchunks. chunks property has three values
- Async – Asynchronously imported modules
- Initial — non-asynchronous imported modules
- All — contains modules for both asynchronous and non-asynchronous imports
// webpack.config.js
const {resolve} = require('path')
module.exports = {
mode: 'production',
entry: {
// index: {import: './src/index.js', dependOn: 'shared'},
// main: {import: './src/main.js', dependOn: 'shared'},
// shared: ['lodash']
index: './src/index.js',
main: './src/main.js'
},
output: {
filename: '[name].bundle.js',
path: resolve(__dirname, 'build')
},
optimization: {
splitChunks: {
chunks: 'all'
}
}
}
Copy the code
More configuration properties of splitChunks can be found on the official website
Dynamic import
Webpack provides two similar techniques when it comes to dynamic code splitting. The first, and recommended option, is to implement dynamic imports using the import() syntax that conforms to the ECMAScript proposal. The second, which is a webPack legacy, uses webPack-specific require.ensure, and we use the first as an example
Export a function as EsModule
// src/util.js
export const test = () => {
console.log('test')
}
Copy the code
Import through import()
Since import() returns a promise, we get the util. Js exported function via.then, which can also be used with async functions
// src/index.js
import('./util').then(res => {
console.log(res)
})
Copy the code
// webpack.config.js
const {resolve} = require('path')
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: '[name].bundle.js',
path: resolve(__dirname, 'build')
}
}
Copy the code
We have now isolated a chunk using dynamic imports
Prefetch/Preload Module
When importing modules dynamically through import(), we need to execute imPRt () before the separated chunks are downloaded and parsed by the browser
Add a click event to the body to dynamically import the test module when the body is clicked
// src/index.js
document.body.addEventListener('click', () => {
import('./test').then(({default: _default}) => {
_default()
})
})
Copy the code
// src/test.js
export default () => {
console.log('test')
}
Copy the code
For the first time to load
After I hit the body
If the module is not very important and the chunk size is large, it will inevitably cause performance problems when the browser downloads and parses it
Webpack V4.6.0 + adds support for prefetching and preloading.
When declaring an import, you can tell webPack to print a “resource hint “to tell the browser, using these built-in directives:
Prefetch: Resources that may be required for some future navigation
Preload: Resources may be required under the current navigation
- Prefetch is performed using prefetch via magic annotations
/* webpackPrefetch: true */
// src/index.js
document.body.addEventListener('click', () => {
import(/* webpackPrefetch: true */'./test').then(({default: _default}) => {
_default()
})
})
Copy the code
You can see that the browser has prefetched chunk, and when you click on the body again, a JS file appears again, only this time the browser is parsing the prefetched JS file
- Preload using preload via magic annotations
/* webpackPreload: true */
// src/index.js
document.body.addEventListener('click', () => {
import(/* webpackPreload: true */'./test').then(({default: _default}) => {
_default()
})
})
Copy the code
The effects of preloading cannot be demonstrated in a browser
conclusion
The preload directive differs from the prefetch directive in a number of ways:
- Preload Chunk starts loading in parallel when the parent chunk loads. Prefetch Chunk starts loading after the parent chunk finishes loading.
- Preload Chunk has medium priority and is downloaded immediately. Prefetch Chunk downloads while the browser is idle.
- Preload chunk is immediately requested in the parent chunk for the present moment. Prefetch Chunk will be used at some point in the future.
- Browser support varies.
Terser
A JavaScript parser for ES6+ and the Mangler/Compressor toolkit Terser help compress the code and make the bundle smaller
yarn add terser
Copy the code
Command execution
Terser is a separate plug-in that can be used from the command line
// src/index.js
class Animal {
constructor(name, age) {
this.name = name
this.age = age
}
}
const dog = new Animal('Fish Ball', 2)
console.log(dog)
Copy the code
npx terser ./src/index.js -o tersermini.js
Copy the code
You can see that a tersermini-js file is output in the root directory
More configurations can be found in the official documentation
terser-webpack-plugin
In Webpack, you can use the TerserPlugin, which uses Terser to compress JavaScript
Webpack V5 comes out of the box with the latest version of the Terser-Webpack-plugin. If you are using WebPack V5 or later and want to customize the configuration, you still need to install the Terser-webpack-plugin. If you use Webpack V4, you must install the version of Terser-webpack-Plugin V4.
yarn add terser-webpack-plugin -D
Copy the code
// webpack.config.js
const {resolve} = require('path')
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: resolve(__dirname, 'build')
},
optimization: {
minimize: true,
minimizer: [
new TerserPlugin(
{
terserOptions: {
compress: {
arrows: false,
collapse_vars: false,
comparisons: false,
computed_props: false,
hoist_funs: false,
hoist_props: false,
hoist_vars: false,
inline: false,
loops: false,
negate_iife: false,
properties: false,
reduce_funcs: false,
reduce_vars: false,
switches: false,
toplevel: false,
typeofs: false,
booleans: true,
if_return: true,
sequences: true,
unused: true,
conditionals: true,
dead_code: true,
evaluate: true
},
mangle: {
safari10: true
}
},
parallel: true,
extractComments: false
}
)
]
}
}
Copy the code
You can refer to the official documentation for detailed configuration
Tree Shaking
Tree shaking is a term used to describe removing dead-code from a JavaScript context. It relies on the statically structured features of ES2015 module syntax, such as import and export. This term and concept was actually popularized by the ES2015 module packaging tool rollup
Webpack implements Tree Shaking in two different ways
- usedExports
- sideEffects
usedExports
UsedExports marks whether certain functions are used and then optimizes them using Terser
SRC add a main entry index.js and a math.js module
The Math module exports two functions
// src/math.js
export function sum(n1, n2) {
return n1 + n2
}
export function square(x) {
return x * x
}
Copy the code
Index main entry file We introduce one of the methods of the Math module
// src/index.js
import {sum} from './math'
console.log(sum(1, 2))
Copy the code
In webpack.config.js, you need to set the mode configuration to development to make sure that the bundle will not be compressed for the most intuitive effect
// webpack.config.js
const {resolve} = require('path')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
module.exports = {
mode: 'development',
devtool: 'source-map',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: resolve(__dirname, 'build')
},
plugins: [
new CleanWebpackPlugin()
]
}
Copy the code
Once packaged, we can see that both methods are included in bundle.js, but we have never referenced the Square method
UsedExports defaults to true in Production mode. Now we manually set usedExports to true in Development mode
// webpack.config.js
module.exports = {
mode: 'development',
devtool: 'source-map',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: resolve(__dirname, 'build')
},
optimization: {
usedExports: true
},
plugins: [
new CleanWebpackPlugin()
]
}
Copy the code
If usedExports is set to true, unused harmony export XXX will be used. Tell Terser that this code can be removed during optimization
UsedExports implements Tree Shaking in conjunction with Terser. Set optimization.minimize to true(the default is true in production mode) and remove dead code.
// webpack.config.js
const {resolve} = require('path')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
mode: 'development',
devtool: 'source-map',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: resolve(__dirname, 'build')
},
optimization: {
usedExports: true,
minimize: true
},
plugins: [
new CleanWebpackPlugin()
]
}
Copy the code
sideEffects
SideEffects, which provides hints to the Webpack Compiler about which files in the project are “pure “so that unused parts of the file can be safely removed
In the main entry file, we simply introduce a test module, but do nothing with it
// src/index.js
import test from './test'
Copy the code
You can see that the test module was introduced in the packaged bundle.js, even though we didn’t use it
If none of the code contains sideEffects, we can tell webpack that it can safely remove the unused export by simply labeling its property value false via package.json’s “sideEffects”
{
"name": "demo",
"sideEffects": false
}
Copy the code
A side effect is defined as code that performs special behavior at import time, rather than just exposing one export or more exports, similar to assigning attributes to global objects.
For example, the following module has side effects
export default {
name: 'test'
}
window._name = 'test'
Copy the code
If your code does have some sideEffects, you can supply the values of sideEffects as an array
{
"name": "demo",
"sideEffects": ["./src/test.js"]
}
Copy the code
This array supports simple Glob pattern matching related files. It uses glob-to-regexp internally (support: *, **, {a,b}, [a-z]). If the matching mode is *. CSS and does not contain /, it is regarded as **/*.css.
All imported files are affected by Tree Shaking. This means that if you use something like CSS-Loader in your project and import a CSS file, you need to add it to the Side Effect list to avoid accidentally removing it in production mode
{
"name": "demo",
"sideEffects": ["./src/test.js", "*.css"]
}
Copy the code
Or set “sideEffects” in the Module. rules configuration option.
export default {
// ...
module: {
rules: [
{
test: /\.css$/i,
use: [
'style-loader',
'css-loader'
],
sideEffects: true
}
]
}
}
Copy the code
conclusion
SideEffects and usedExports (more commonly known as Tree shaking) are two different ways to optimize
UsedExports relies on Terser to detect side effects in statements. It’s a JavaScript task and it’s not as straightforward as sideEffects. And it cannot jump the rotor tree/dependency because the bylaws say side effects need to be evaluated
SideEffects is more efficient because it allows skipping entire modules/files and entire file subtrees
Some plug-ins for handling CSS
We’ve already used a few loaders for CSS handling, so let’s optimize our CSS with a few plug-ins
MiniCssExtractPlugin
Previously, we used CSS-loader, style-loader and other loaders to inject CSS into DOM instead of extracting CSS into a separate file. Mini-css-extract-plugin This plug-in extracts CSS into a separate file. It creates a CSS file for each JS file that contains CSS. It supports loading CSS and SourceMap on demand
yarn add mini-css-extract-plugin -D
Copy the code
// src/style.index.css
html,
body {
width: 100%;
height: 100%;
overflow: hidden;
}
.caption {
display: flex;
align-items: center;
justify-content: center;
color: #ddd;
}
Copy the code
// src/index.js
import './style/index.css'
Copy the code
The MiniCssExtractPlugin needs to be used in two places in the WebPack configuration, one is plugins, Another place is in the module. The rules used in the configuration options MiniCssExtractPlugin. Loader replace our previous style – loader
// webpack.config.js const {resolve} = require('path') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const {CleanWebpackPlugin} = require('clean-webpack-plugin') module.exports = { mode: 'production', entry: './src/index.js', output: { filename: 'bundle.js', path: resolve(__dirname, 'build') }, module: { rules: [ { test: /\.css$/i, use: [/ / use MiniCssExtractPlugin. Replace style - loader loader MiniCssExtractPlugin. Loader, // 'style-loader' 'css-loader']}]}, plugins: [// Use MiniCssExtractPlugin new MiniCssExtractPlugin({// Determine the name of each output CSS file filename: 'CSS /[name][contenthash:6].css', // Determine the name of each output CSS block file chunkFilename: 'css/[id][contenthash:6].css' }), new HtmlWebpackPlugin({ template: './src/index.html' }), new CleanWebpackPlugin() ] }Copy the code
According to the above configuration, we can find that there is a CSS folder under the build package file, and the CSS file under the CSS folder is the CSS file we just removed through the configuration package
MiniCssExtractPlugin also has many configuration parameters. For more parameters, please refer to the official website
PurgeCSS
PurgeCSS is a tool for removing unused CSS code. Use it as part of your development process. When you are building a website, you may decide to use a CSS framework such as TailwindCSS, Bootstrap, MaterializeCSS, Foundation, etc. However, you only use a small part of the framework and a lot of CSS styles are not used. PurgeCSS analyzes your content and CSS files. First it matches the selectors used in the CSS file with the selectors in the content file. Then it removes the unused selectors from the CSS to produce smaller CSS files
PurgeCSS is a separate tool for optimizing CSS in our projects using purgecss-webpack-Plugin in webPack configuration
yarn add purgecss-webpack-plugin -D
Copy the code
// src/style/index.css
html,
body {
width: 100%;
height: 100%;
overflow: hidden;
}
.caption {
display: flex;
align-items: center;
justify-content: center;
color: #ddd;
}
.wrapper {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
Copy the code
Add a Div tag with the Caption class style name to the index.html template file
// src/index.html <! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, "> <title>Document</title> </head> <body> <div class=" Caption ">PurgeCSS</div> </body> </ HTML >Copy the code
// src/index.js
import './style/index.css'
Copy the code
With the WebPack plug-in, you can specify the content to be parsed by purgecSS by setting an array of file names. These can be HTML, PUG, Blade, and so on. You can also use modules like glob or glob-all to easily get a list of files
yarn add glob -D
Copy the code
// webpack.config.js const {resolve} = require('path') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const {CleanWebpackPlugin} = require('clean-webpack-plugin') const PurgecssPlugin = require('purgecss-webpack-plugin') const glob = require('glob') module.exports = { mode: 'production', entry: './src/index.js', output: { filename: 'bundle.js', path: resolve(__dirname, 'build') }, module: { rules: [ { test: /\.css$/i, use: [ MiniCssExtractPlugin.loader, 'css-loader' ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: 'css/[name][contenthash:6].css', chunkFilename: 'CSS /[id][contenthash:6].css'}), new PurgecssPlugin({// Match file paths: glob.sync(' ${__dirname}/**/*', {nodir: true}) }), new HtmlWebpackPlugin({ template: './src/index.html' }), new CleanWebpackPlugin() ] }Copy the code
After packaging, we can see that if we don’t use the CSS, we won’t be packaging because we don’t use the.wrapper class style name at all, so it won’t be included
CssMinimizerPlugin
This plugin uses CSsnano to optimize and shrink your CSS
yarn add css-minimizer-webpack-plugin -D
Copy the code
In the WebPack configuration, configure it in optimization.minimizer
// webpack.config.js const {resolve} = require('path') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const {CleanWebpackPlugin} = require('clean-webpack-plugin') const PurgecssPlugin = require('purgecss-webpack-plugin') const glob = require('glob') const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') module.exports = { mode: 'production', entry: './src/index.js', output: { filename: 'bundle.js', path: resolve(__dirname, 'build') }, module: { rules: [ { test: /\.css$/i, use: [ MiniCssExtractPlugin.loader, 'css-loader' ] } ] }, optimization: { minimizer: New CssMinimizerPlugin()]}, plugins: [new MiniCssExtractPlugin({filename: 'css/[name][contenthash:6].css', chunkFilename: 'css/[id][contenthash:6].css' }), new PurgecssPlugin({ paths: glob.sync(`${__dirname}/**/*`, {nodir: true}) }), new HtmlWebpackPlugin({ template: './src/index.html' }), new CleanWebpackPlugin() ] }Copy the code
HTTP compression
HTTP compression refers to the method of transferring compressed text content between a Web server and a browser. HTTP compression usually uses the GZIP compression algorithm to compress HTML, JavaScript, and CSS files. The biggest benefit of compression is that it reduces the amount of data transferred over the network, thus improving the access speed of the client browser. Of course, it also adds a bit of burden to the server.
HTTP compression process
- The browser sends an Http request to the Web server, which contains accept-Encoding: gzip, deflate. (Tell the server that the browser supports gzip compression)
- After receiving the request, the Web server generates the original Response, including the original content-type and content-Length
- The Web server encodes the Response using Gzip. The header contains content-Type and content-Length, and the content-encoding: Gzip. The Response is then sent to the browser
- When the browser receives the Response, it decodes it according to Content-Encoding:gzip. After getting the original response, the page is displayed
What can WebPack do about HTTP compression
The server needs to consume certain resources to compress files. In fact, webpack can compress files and generate corresponding compressed files when packaging. If gzip compression is enabled on the server, the server does not need to compress and just returns the corresponding compressed file that we have packaged, which also takes a little bit of the burden off the server
CompressionPlugin
The CompressionPlugin can do this
yarn compression-webpack-plugin -D
Copy the code
// src/index.js
import _ from 'lodash'
console.log(_.add(6, 4))
Copy the code
// webpack.config.js const {resolve} = require('path') const CompressionPlugin = require('compression-webpack-plugin') module.exports = { mode: 'production', entry: './src/index.js', output: { filename: '[name].js', path: Resolve (__dirname, 'build')}, plugins: [new CompressionPlugin ({matching need compressed file test: / / / \ | js (CSS) $/})]}Copy the code
CompressionPlugin has many configuration options available on the CompressionPlugin website