Write it up front
Recently, I have been using Webpack a lot in my work, so I want to learn it systematically. I found the tutorial of Geek Time and learned it by following the video, and gained a lot. Although the video tutorial came out in 2019, it is a little old and some of the knowledge is outdated, but it does not prevent learning. All outdated knowledge should be known and searched for alternatives. Learning resources: Playing with Webpack, here are my learning notes.
Build tools
The React JSX, Vue and Angular directives, CSS preprocessors, the latest ES syntax, etc., are constantly evolving that browsers don’t recognize.
What the build tool does is convert the syntax that the browser does not recognize (advanced syntax) into the syntax that the browser does recognize (low-level syntax).
It also confuses code compression, making it harder to read while reducing the size of the code.
Webpack is now a popular build tool on the front end.
I metwebpack
configurationscript
The script
Once webPack is installed, there are two ways to use webpack on the command line:
- The specified path
./node_modules/.bin/webpack
- use
npx
Tools,npx webpack
These two methods are quite cumbersome to use. There is an easy way to use scripts in package.json to read commands in node_modules/.bin.
Run NPM run build from the command line.
"scripts": {
"build": "webpack"
}
Copy the code
The configuration file
The default configuration file for Webpack, webpack.config.js, can be specified via webpack –config.
For example, the production configuration file is webpack.config.prod.js, and the development configuration file is webpack.config.dev.js.
"scripts": {
"build": "webpack --config webpack.config.prod.js"."build:dev": "webpack --config webpack.config.dev.js"
}
Copy the code
The core concept
Webpack has five core concepts: Entry, Output, Mode, loaders, and plugins.
entry
/ SRC /index.js by default./ SRC /index.js
Entry is available in two ways: single entry and multiple entry.
Single entry is used for single page applications, and entry is a string
module.exports = {
entry: "./src/index.js"};Copy the code
Multi-entry is used for multi-page applications. Entry is an object
module.exports = {
entry: {
index: "./src/index.js".app: "./src/app.js",}};Copy the code
output
The packaged output file is./dist/main.js by default.
Entry is a single entry. Output can be modified by modifying path and filename.
const path = require("path");
module.exports = {
entry: "./src/index.js".output: {
path: path.resolve(__dirname, "dist"),
filename: "index.js",}};Copy the code
There are multiple entry entries. Filename of output needs to use [name] placeholder to specify the name after packaging, which corresponds to the key in the entry
const path = require("path");
module.exports = {
entry: {
index: "./src/index.js".app: "./src/app.js",},output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].js",}};Copy the code
mode
It can be set to development, production, or None. The default is production.
development
: Development environment, Settingsprocess.env.NODE_ENV
The value ofdevelopment
. openNamedChunksPlugin
andNamedModulesPlugin
production
: Production environment, Settingsprocess.env.NODE_ENV
The value ofproduction
. openFlagDependencyUsagePlugin
.FlagIncludedChunksPlugin
.ModuleConcatenationPlugin
.NoEmitonErrorsPlugin
.OccurrenceOrderPlugin
.SideEffectsFlagPlugin
andTerserPlugin
none
: No optimization options are enabled
Ps: During the hot update phase of the code, the two plug-ins opened in development mode will print out on the console which module has been hot updated and what is the path of this module. Plug-ins opened in Production mode compress the code.
loaders
Webpack only supports JS and JSON file types. Loader is used to process other files and add them to dependency diagrams.
Loader is a function that takes the source file as an argument and returns the converted result.
Generally, loaders are named with the -loader suffix, such as CSS-loader and babel-loader.
Test specifies the matching rule, and use specifies the name of the loader to use
module.exports = {
module: {
rules: [{test: /.less$/,
use: ["style-loader"."css-loader"."less-loader"],},],},};Copy the code
A loader usually does only one thing. When parsing less, it needs to use less-Loader to convert less into CSS. Since WebPack cannot recognize CSS, it needs to use CSS-Loader to convert CSS into CommonJS objects and put them into JS. Finally, you need to use style-loader to insert the CSS into the style of the page.
Ps: Loaders are typically composed in two ways, one from left to right (similar to Unix pipes) and the other from right to left (compose). Webpack selects Compose and executes the loader once from right to left
plugins
Plugin is used for file optimization, resource management, environment variable injection, and the entire build process.
Plugins are usually named with the suffix -webpack-plugin, and some are named with the suffix -plugin.
const CleanWebpackPlugin = require("clean-webpack-plugin");
module.exports = {
plugins: [new CleanWebpackPlugin()],
};
Copy the code
webpack
Resource parsing
parsinges6
Need to install @babel/core, @babel/preset-env, babel-loader
Create a new. Babelrc file under the root directory and add @babel/preset-env to the presets
{
"presets": ["@babel/preset-env"]}Copy the code
Configure babel-loader in webpack
module.exports = {
module: {
rules: [{test: /.js$/,
use: "babel-loader",},],},};Copy the code
There is another way to configure it, instead of using the. Babelrc file, configure it in the options parameter of use
module.exports = {
module: {
rules: [{test: /.js$/,
use: {
loader: "babel-loader".options: {
presets: ["@babel/preset-env"],},},},],},};Copy the code
parsingcss
You need to install CSS-loader and style-loader.
module.exports = {
moudle: {
rules: [{test: /\.css$/,
use: ["style-loader"."css-loader"],},],},};Copy the code
parsingless
Less-loader, CSS-loader, and style-loader need to be installed
module.exports = {
module: {
rules: [{test: /\.less$/,
use: ["style-loader"."css-loader"."less-loader"],},],},};Copy the code
Parse images and fonts
The file-loader needs to be installed
module.exports = {
module: {
rules: [{test: /.(png|jpeg|jpg|gif)$/,
use: ["file-loader"],}, {test: /.(woff|woff2|eot|ttf|otf)$/,
use: ["file-loader"],},],},};Copy the code
url-loader
Url-loader can convert smaller images to Base64.
module.exports = {
module: {
rules: [{test: /.(png|jpeg|jpg|gif)$/,
use: {
loader: "url-loader".options: {
limit: 10240.// For images smaller than 10K, WebPack automatically converts to Base64 when packing},},},],},};Copy the code
configurationvue
To configure the VUE development environment, install VUE, VUE-Loader, and vue-template-Compiler
const { VueLoaderPlugin } = require("vue-loader");
module.export = {
plugins: [new VueLoaderPlugin()],
module: {
rules: [{test: /\.vue$/,
use: "vue-loader",},],},};Copy the code
Ps: The vue-Loader used here is version 15.x. I have a problem installing the latest version 16.x, which has not been solved.
configurationreact
Install react, react-dom, @babel/preset-react to configure the React development environment
module.exports = {
module: {
rules: [{test: /\.js$/,
use: {
loader: "babel-loader".options: {
presets: ["@babel/preset-env"."@babel/preset-react"],},},},],},};Copy the code
webpack
File monitoring and hot update
File to monitor
Every time the code is modified, it needs to be built manually, affecting development efficiency. Webpack provides file listening. When listening is enabled, WebPack calls the FS module in Node to determine if the file has changed, and if it has, it will automatically rebuild and output the new file.
Webpack enables listening mode in two ways :(manually refresh the browser)
- Start the
webpack
When ordered, take it--watch
parameter
"scripts": {
"watch": "webpack --watch"
}
Copy the code
webpack.config.js
Set in thewatch: true
module.exports = {
watch: true};Copy the code
File listening analysis
The Webpack file listens to see if the last edit time of the file has changed.
It saves the modified time, and when the file is modified, it compares it with the last modified time.
If it finds an inconsistency, it does not immediately tell the listener. Instead, it caches the changes to the file, waits for a period of time, and builds a list of files from that time together if other files change during that time. This waiting time is called the aggregateTimeout.
module.exports = {
watch: true.watchOptions: {
ignored: /node_modules/, aggregateTimeout:300.poll: 1000,}}Copy the code
watch
Default for:false
watchOptions
: onlywatch
fortrue
, takes effectignored
: Files or folders to be listened to are ignored. The default value is empty and ignoredThe node - modules
Performance will improve.aggregateTimeout
: Time to wait after listening for file changes, default300ms
poll
: Polling time,1s
At a time
Hot update
Hot update need webpack – dev server and HotModuleReplacementPlugin use behind two plug-ins.
Compared to Watch, it doesn’t output files and goes straight to memory, so it builds faster to read.
Hot updates are only used in development mode.
const webpack = require("webpack");
const path = require("path");
module.exports = {
mode: "development".plugins: [new webpack.HotModuleReplacementPlugin()],
devServer: {
contentBase: path.join(__dirname, "dist"),
hot: ture,
},
};
Copy the code
Configure commands in package.json
webpack4.x
"scripts": {
"dev": "webpack-dev-server --open"
}
Copy the code
webpack5.x
"scripts": {
"dev": "webpack server"
}
Copy the code
Ps: Webpack5. x conflicts with webpack-dev-server. –open cannot be used to open the browser.
HotModuleReplacementPlugin
The core of hot update is HMR Server and HMR Runtime.
HMR Server
: is the server used to changejs
Module bywebsocket
Notifies the browser side of the messageHMR Runtime
: is the browser side, used for receivingHMR Server
The passed module data is visible to the browser.hot-update.json
file
Hot-module-altern-plugin functions: Webpack itself to build up the bundle of js itself is not have hot update, the role of HotModuleReplacementPlugin is HMR Runtime into bundles. Js. Enables bundle.js to establish websocket communication with HMR Server. Once a file on disk is modified, HMR Server sends the js module with the modification to HMR Runtime via websocket. HMR Runtime then updates the page code locally without refreshing the browser.
Webpack-dev-server provides the ability of the bundle server to generate bundle.js that can be accessed via localhost:// XXX, as well as livereload (browser auto-refresh).
File fingerprint
What is a document fingerprint
File fingerprint refers to the suffix of the output file name after packaging. For example, index_56d51795.js.
It is usually used for version management
File fingerprint type
hash
: Relates to the construction of the project, whenever the project files change, the construction of the projecthash
The value will change. usinghash
Computatively, the hash value is different after each build, and if the contents of the file have not changed, it is not possible to cache this way.chunkhash
And:webpack
packagedchunk
Relevant, differententry
It’s going to be differentchunkhash
. The production environment will separate some common libraries from the source code and use them separatelychunkhash
Build, as long as the code of the common library does not change, its hash value does not change, can implement caching.contenthash
: Defined according to the file contenthash
, the file content remains unchanged, thencontenthash
The same. Commonly used incss
Resources, ifcss
Resource usechunkhash
, so modifiedjs
.css
The resource would change, the cache would fail, socss
usecontenthash
.
ps:
- use
hash
The scene should be combinedmode
Let’s think about ifmode
isdevelopment
And in the use ofHMR
In case of usechunkhash
Is not suitable, should be usedhash
. whilemode
isproduction
Should be usedchunkhash
.js
usechunkhash
It’s easy to find resources becausejs
The resource correlation degree is higher.css
usecontenthash
becausecss
It’s usually written on different pages,css
The correlation between resources is not high, so there is no need to update other resources when they are modifiedcss
.
js
File fingerprint
To set filename for output, use [chunkhash],
const path = require("path");
module.exports = {
output: {
path: path.join(__dirname, "dist"),
filename: "[name]_[chunkhash:8].js".// Take the first eight bits of the hash}};Copy the code
css
File fingerprint
The Mini-CSS-extract-plugin needs to be installed
Style-loader inserts CSS into the head of a page, and mini-css-extract-plugin extracts into a separate file. They are mutually exclusive.
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
module: {
rules: [{test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"],},],},plugins: [
new MiniCssExtractPlugin({
filename: "[name]_[contenthash:8].css",})]};Copy the code
Image file fingerprint
The file-loader needs to be installed
Placeholder name | meaning |
---|---|
[ext] |
Resource name extension |
[name] |
The file name |
[path] |
The relative path of the file |
[folder] |
The folder where the file resides |
[contenthash] |
document-contenthash The default ismd5 |
[hash] |
document-contenthash The default ismd5 |
[emoji] |
A random reference to the contents of the fileemoji |
The concept of image hash is different from that of CSS/JS. The hash of an image is determined by the content of the image.
module.exports = {
module: {
rules: [{test: /.(png|jpeg|jpg|gif)$/,
use: {
loader: "file-loader".options: {
filename: "[folder]/[name]_[hash:8].[ext]",},},}, {test: /.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: "file-loader".options: {
filename: "[name]_[hash:8].[ext]",},},],},},};Copy the code
Code compression
Code compression is mainly divided into JS compression, CSS compression, HTML compression.
js
The compression
Webpack has built-in uglifyjs-webpack-plugin, default packaged files are compressed, there is no need for additional configuration.
You can install the plug-in manually, and you can set additional configurations, such as parallel compression, to true
const UglifyjsWebpackPlugin = require("uglifyjs-webpack-plugin");
module.exports = {
optimization: {
minimizer: [
new UglifyjsWebpackPlugin({
parallel: true,}),],},};Copy the code
css
The compression
Install the optimize- CSS -webpack-plugin and CSS preprocessor CSsnano.
const OptimizeCssWebpackPlugin = require("optimize-css-webpack-plugin")
module.exports = {
plugins: [
new OptimizeCssWebpackPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require("cssnano) }) ] }Copy the code
html
The compression
The htML-webpack-plugin needs to be installed by setting compression parameters.
New HtmlWebpackPlugin({… })
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, "src/search.html"), // The location of the HTML template, which can use EJS syntax
filename: "search.html".// The packaged HTML name
chunks: ["search"].// The packaged HTML will use those chunks
inject: true.// Set to true, the packed JS CSS is automatically injected into the HTML
minify: {
html5: true.collapseWhitespace: true.preserveLineBreaks: false.minifyCSS: true.minifyJS: true.removeComments: false
}),
newHtmlWebpackPlugin({... })]};Copy the code
HtmlWebpackPlugin minifyJS and minifyCSS in minify are used to compress JS and CSS inline in HTML from the start (ES6 syntax is not allowed).
Chunks correspond to keys in the entry. Which chunk you want to automatically inject will be written to the chunks.
Differences between Chunk, bundle, and Module:
chunk
: eachchunk
Is more thanmodule
Composition, which can be divided into multiple parts by codechunk
bundle
: Final file generated by packagingmodule
:webpack
Module (js
,css
, the picture)
Automatically clean up the build directory
Each time a build is made, new files are generated, resulting in more and more output files in the output directory.
The most common cleaning method is to use the rm command to delete the dist directory. Before packaging, execute the rm -rf command to delete the dist directory and then package it.
"scripts": {
"build": "rm -rf ./dist && webpack"
}
Copy the code
Another option is to use Rimraf for deletion.
To install Rimraf, delete the dist directory before packing, and then pack.
"scripts": {
"build": "rimraf ./dist && webpack"
}
Copy the code
Both of these solutions clean up the DIST directory, but they’re not very elegant.
Webpack provides the clean-webpack-plugin, which automatically cleans files under output.path.
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
plugins: [new CleanWebpackPlugin()],
};
Copy the code
Autocomplete style prefixes
Different browser vendors have different standards for implementing CSS features. For example, display: flex is written as display: -webkit-box in the WebKit kernel.
Adding cores one by one during development would be a huge undertaking.
Webpack can use loader to solve the problem of automatically completing CSS prefixes.
Install postCSS-Loader and its plug-in autoprefixer.
Autoprefixer is prefixed by the CSS compatibility provided by can I Use.
Autoprefixer is a post-processor, which is different from a preprocessor, which is processed at packaging time, and autoprefixer, which is processed after the code has been processed and the style has been generated.
Install [email protected], [email protected] in WebPack 4.x.
Method 1: Directly configure the configuration in webpack.config.js
Webpack. Config. Js:
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
module: {
rules: [{test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader"."less-loader",
{
loader: "postcss-loader".options: {
plugins: () = > [
require("autoprefixer") ({overrideBrowserslist: ["last 2 version"."1%" >."ios 7"].// The latest two versions, more than 1% users and compatible with ios7}),],},},],},],},},};Copy the code
Method 2: Use the postCSS configuration file postcss.config.js and webpack.config.js to write postCSs-loader directly.
Webpack. Config. Js:
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
module: {
rules: [{test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader"."less-loader"."postcss-loader",],},],},};Copy the code
Postcss. Config. Js:
module.exports = {
plugins: [
require("autoprefixer") ({overrideBrowserslist: ["last 2 version"."1%" >."ios 7"],}),]};Copy the code
Method 3: The browser compatibility can be written in package.json, postcss.config.js only need to load autofixer.
Webpack. Config. Js:
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
module: {
rules: [{test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader"."less-loader"."postcss-loader",],},],},};Copy the code
Postcss. Config. Js:
module.exports = {
plugins: [require("autofixer")]};Copy the code
Package. Json:
"browserslist": {
"last 2 version"."1%" >."ios 7"
}
Copy the code
Resources inline
Some scenarios require resource inlining. Common scenarios include:
- Initialization script for the page frame
- To reduce
http
Network request, turn some small pictures intobase64
Content into code, less requests css
Inlining increases the page experience- Early executed
js
, such asREM
plan
html
inline
In multi-page projects, there are many common tags in the head, such as meta, which can be extracted as a template and referenced to improve maintainability.
Install [email protected]
<head>${require("raw-loader! ./meta.html")}</head>
Copy the code
js
inline
In REM, the font size of the HTML tag is required as soon as possible, so the JS section should be loaded and executed as soon as possible.
<head>
<script>The ${require('raw-loader! babel-loader! ./calc-root-fontsize.js')}
</script>
</head>
Copy the code
css
inline
For a better experience and to avoid page flickering, you need to inline the packed CSS into the head.
Install the html-inline-CSS-webpack-plugin, which needs to be placed after the htMl-webpack-plugin.
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const HtmlInlineCssWebpackPlugin = require("html-inline-css-webpack-plugin");
module.exports = {
module: {
rules: [MiniCssExtractPlugin.loader, "css-loader"."less-loader"],},plugins: [
new MiniCssExtractPlugin({
filename: "[name]_[contenthash:8].css",}).new HtmlWebpackPlugin(),
new HtmlInlineCssWebpackPlugin(),
],
};
Copy the code
You need to extract the CSS into separate files first.
The difference between style-laoder and html-inline-CSS-webpack-plugin is as follows:
style-loader
Insert styling is a dynamic process that does not exist in packaged source codestyle
Tag, passed at execution timejs
Dynamic insertstyle
The labelhtml-inline-css-webpack-plugin
: will be built at build timecss
Inserted into the pagestyle
In the label
Multi-page application
Each page has an entry and an HTMl-webpack-plugin in plugins.
One drawback to this approach is that each entry adds an HTML-webpack-plguin.
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: {
index: "./src/index.js".search: "./src/search.js",},plugins: [
new HtmlWebpackPlugin({ template: "./src/index.html" }),
new HtmlWebpackPlugin({ template: "./src/search.html"})]};Copy the code
With the help of glob, the configuration can be generalized.
Install glob with all pages placed under SRC and entry files called index.js.
const path = require("path");
const glob = require("glob");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const setMPA = () = > {
const entry = {};
const htmlWebpackPlugins = [];
const entryFiles = glob.sync(path.join(__dirname, "./src/*/index.js"));
entryFiles.forEach((entryFile) = > {
const match = entryFile.match(/src\/(.*)\/index\.js/);
const pageName = match && match[1];
entry[pageName] = entryFile;
htmlWebpackPlugins.push(
new HtmlWebpackPlugin({
template: path.join(__dirname, `src/${pageName}/index.html`),
filename: `${pageName}.html`.chunks: [pageName],
inject: true.minify: {
html5: true.collapseWhitespace: true.preserveLineBreaks: false.minifyCSS: true.minifyJS: true.removeComments: false,}})); });return {
entry,
htmlWebpackPlugins,
};
};
const { entry, htmlWebpackPlugins } = setMPA();
module.exports = {
entry,
plugins: [].concat(htmlWebpackPlugins),
};
Copy the code
sourceMap
Method of use
Code published to the build environment is compressed and obfuscated, but after the compression and obfuscation, the code is like reading gobbledegook.
SourceMap provides a mapping between compressed code and source code.
SourceMap is turned on in the development environment and turned off in the online environment, and the Source Map is uploaded to the error monitoring system during troubleshooting online.
source map
The keyword
eval
Use:eval
Package module codesource map
Produced:map
filecheap
: Does not contain column informationinline
Will:.map
As aDataURI
Embedded, not generated separately.map
filemodule
: containsloader
thesourcemap
source map
type
devtool | For the first time to build | Secondary building | Suitability for production environment | Locatable code |
---|---|---|---|---|
(none) |
+++ | +++ | yes | The final output code |
eval |
+++ | +++ | no | webpack Generated code (module by module) |
cheap-eval-source-map |
+ | ++ | no | Loader converted code (lines only) |
cheap-module-eval-source-map |
o | ++ | no | Source code (lines only) |
eval-source-map |
— | + | no | The source code |
cheap-source-map |
+ | o | yes | afterloader Converted code only sees lines.) |
cheap-module-source-map |
o | – | yes | Source code (lines only) |
inline-cheap-source-map |
+ | – | no | afterloader Transformed code (lines only) |
inline-cheap-module-source-map |
o | – | no | Source code (lines only) |
source-map |
— | — | yes | The source code |
Inline-source-map |
— | — | no | The source code |
hidden-source- map |
— | — | yes | The source code |
Extract page common resources
In the project, several basic libraries, such as React and React-DOM, as well as some common code, are used in multiple pages. When packaging, these will be packaged into the final code, which is wasteful and bulky.
Webpack4 has the SplitChunksPlugin plugin built in.
Chunks parameter description:
async
Asynchronously imported libraries decouple (default)initial
Synchronously introduce libraries for separationall
Separation of all imported libraries (recommended)
Put filename in chunks in htMl-webpack-plugin by pulling the name of the base library from cacheGroups and importing it automatically:
module.exports = {
plugins: [
new HtmlWebpackPlugin({
chunks: ["vendors"."commons"."index"].// The packaged HTML will use those chunks})); ] .optimization: {
splitChunks: {
chunks: "async".minSize: 30000.// The smallest size, in bytes, of the public package to be removed
maxSize: 0.// Maximum size
minChunks: 1.// The number of times the resource was used (on multiple pages), greater than 1, the minimum number of times
maxAsyncRequests: 5.// Number of concurrent requests
maxInitialRequests: 3.// The entry file can be divided into up to 3 js files
automaticNameDelimiter: "~".// The connection when the file is generated
automaticNameMaxLength: 30.// Automatic automatic naming maximum length
name: true.// Make the name set in cacheGroups valid
cacheGroups: {
// The above parameters take effect when packaging synchronized code
vendors: {
test: /[\\/]node_modules[\\/]/.// Check whether the imported library is in the node_modlues directory
priority: -10.// The greater the value, the higher the priority. Modules are first packaged into high-priority groups
filename: "vendors.js".// Package all the libraries into a file called vendors. Js
},
default: {
minChunks: 2./ / it has
priority: -20./ / it has
reuseExistingChunk: true.// If a module has already been packaged, ignore the module when repackaged},},},},};Copy the code
tree shaking
(Tree shaking)
There are multiple methods in a module, and methods that are used are packaged into bundles, while methods that are not used are not.
Tree shaking is packaging only used methods into the bundle, and the rest are erased during the Uglify phase.
Webpack supports modules: false in.babelrc by default. The Production phase is enabled by default.
Must be ES6 syntax, CJS mode is not supported.
If you want to transform ES6 into ES5 and turn tree shaking on, you need to set module: false in. Babelrc. Otherwise, Babel will transform ES6 into CJS by default.
DCE
(Deal Code Elimination)
DEC elimination is the elimination of dead code.
- The code will not be executed
if (false) {
console.log("Code will not be executed.");
}
Copy the code
- The results of code execution are not used
function a() {
return "this is a";
}
let A = a();
Copy the code
- Code is written, not read
let a = 1;
a = 2;
Copy the code
tree shaking
The principle of
At compile time, it is important to determine whether code is used or not. It is not possible to analyze what code is used or not at runtime. Tree Shaking marks unused code with comments and removes it at uglify.
Using the features of ES6 module:
- Can only appear as a statement at the top level of a module
import
Can only be string constantsimport binding
isimmutable
Delete uselesscss
purgecss-webpack-plugin
: Walks through the code to identify what is already usedcss class
- and
mini-css-extract-plugin
Together with
- and
uncss
:html
Need to pass throughjsdom
Load, all styles passpostCSS
Parse, passdocument.querySelector
To identify thehtml
Selector not found in file
const PurgecssPlugin = require("purgecss-webpack-plugin");
const PATHS = {
src: path.join(__dirname, "src"),};module.exports = {
plugins: [
new PurgecssPlugin({
paths: glob.sync(`${PATHS.src}/ * * / * `, { nodir: true})}),]};Copy the code
scope hoisting
Use and principle analysis
Building enough code results in a large number of closures, as shown in the following figure:
When an external module is introduced, it is wrapped in a function. As more modules are introduced, a large amount of function-wrapped code will be generated, resulting in larger volume. At runtime, the memory overhead will be larger due to the more scoped functions created.
- be
webpack
The converted module is wrapped in a layer import
Will be converted to__webpack_require
Analysis of the
- What came out was one
IIFE
(Anonymous closure) modules
Is an array, and each entry is a module initialization function__webpack_require
Used to load the module, returnsmodule.exports
- through
WEBPACK_REQUIRE_METHOD(0)
Start the program
scope hoisting
The principle of
Place all module code in a function scope in reference order, and then rename some variables appropriately to prevent variable name conflicts.
Module calls have sequence, module A calls module B, because there are function packages, the order of module A and module B does not matter. If the package code is eliminated, modules need to be discharged according to the reference order of modules, and module B should be placed before module A, so that module B can be called by module A.
Scope collieries can reduce function declarations and memory overhead. The Production phase is enabled by default.
Must be ES6 syntax, CJS does not support.
Ps: Scope reactorizes multiple scopes into one scope. When a module is referenced more than once, it has no effect. If a module is referenced more than once, the module’s code is inlined multiple times, increasing the size of the packaged file.
useESLint
ESLint can unify the team’s code style and help find bugs.
Two ways to use:
- with
CI/CD
integration - with
webpack
integration
webpack
withCI/CD
integration
Husky needs to be installed.
Add scripts and modify files through lint-staged checks.
"scripts": {
"precommit": "lint-staged"
},
"lint-staged": {
"linters": {
"*.{js,less}": ["eslint --fix"."git add"]}}Copy the code
webpack
withESLint
integration
With eslint-Loader, the build is to check the JS specification
Install plugins babel-eslint, eslint, eslint-config-airbnb, eslint-config-airbnb-base, eslint-loader, eslint-plugin-import, eslint-plugin- JSX – ally, eslint – plugin – react.
Create the.eslintrc.js file
module.exports = {
parser: "babel-eslint".extends: "airbnb".env: {
browser: true.node: true,},rules: {
indent: ["error".2],}};Copy the code
The webpack.config.js file configures the eslint-loader
module.exports = {
module: {
rules: [{test: /\.js$/,
use: ["babel-loader"."eslint-loader"],},],},};Copy the code
Code segmentation and dynamicsimport
For large Web applications, putting all of your code in one file is not efficient enough, especially if some of your code blocks are only used in special cases.
Webpack has a feature that breaks your code into chunks and loads them when the code runs and needs them.
Usage Scenarios:
- Pull the same code into a shared block
- The script loads lazily, making the initial download smaller
Lazy way to load JS scripts
CJS
:require.ensure
ES6
:import
(Currently no native support, yesbable
Translation)
Install the @bable/plugin-syntax-dynamic-import plugin
Babelrc file:
{
plugins: ["@bable/plugin-syntax-dynamic-import"];
}
Copy the code
Example:
class Index extends React.Component {
constructor() {
super(... arguments);this.state = {
Text: null}; } loadComponent =() = > {
import("./text.js").then((Text) = > {
this.setState({ Text: Text.default });
});
};
render() {
const { Text } = this.state;
return (
<div className="search">
react1
{Text && <Text />}
<div onClick={this.loadComponent}>Am I</div>
<img src={img} alt="" />
</div>); }}Copy the code
One thing to note here is that when cacheGroups is configured and minChunks are set to 1, the lazy loading script set above does not take effect because the import is statically analyzed at load time.
cacheGroups: {
commons: {
name: "commons".chunks: "all".priority: -20.// The greater the value, the higher the priority. Modules are first packaged into high-priority groups
minChunks: 1,}}Copy the code
Multi-process/multi-instance: parallel compression
Method 1: Use the webpack-parallel-ugli-fi -plugin
const WebpackParalleUglifyPlugin = require("webpack-parallel-uglify-plugin");
module.exports = {
plugins: [
new WebpackParalleUglifyPlugin({
uglifyJs: {
output: {
beautify: false.comments: false,},compress: {
warnings: false.drop_console: true.collapse_vars: true.reduce_vars: true,},},}),],};Copy the code
Method 2: Use uglifyjs-webapck-plugin to enable the PARALLEL parameter
const UglifyJsPlugin = require("uglifyjs-webpack-plugin")
modules.exports = {
plugins: [
UglifyJsPlugin: {
warnings: false.parse: {},
compress: {},
mangle: true.output: null.toplevel: false.nameCache: null.ie8: false.keep_fnames: false,},parallel: true]}Copy the code
Terser-webpack-plugin enables parallel (recommended by Webpack4)
const TerserPlugin = require("terser-webpack-plugin");
module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
parallel: 4,}),],},};Copy the code
Increase build speed
React, react-dom, redux, react-Redux base package, and service base package into a file
Method: The DLLPlugin is used for subcontracting, and the DLLReferencePlugin references manifest.json
useDLLPlugin
In the subcontract
const path = require("path");
const webpack = require("webpack");
module.exports = {
context: process.cwd(),
resolve: {
extensions: [".js".".jsx".".json".".less".".css"].modules: [__dirname, "nodu_modules"],},entry: {
library: ["react"."react-dom"."redux"."react-redux"],},output: {
filename: "[name].dll.js".path: path.resolve(__dirname, "./build/library"),
library: "[name]",},plugins: [
new webpack.DllPlugin({
name: "[name]".path: "./build/library/[name].json",})]};Copy the code
Introduced in webpack. Config. Js
module.exports = {
plugins: [
new webapck.DllReferencePlugin({
manifest: require("./build/library/manifest.json"),})]};Copy the code
With the use of Webpack4 in the project, the dependency on DLLS is not so big, and the improvement of DLLS is not particularly obvious, while the hard-source-webpack-plugin can greatly improve the secondary build. However, in a real front-end factory, DLLS are still necessary. For a team, it’s basically the same technology stack, react or VUE. At this point, it is common practice to bundle the common framework into a common bundle for all projects to use. DLLS are perfect for this scenario: grouping multiple NPM packages into a common package. Therefore, it is still valuable to use DLLS for subcontracting projects within the team.
SplitChunks can also be used to do the DllPlugin, but it is recommended to use splitChunks to extract common JS files between pages, because it takes time to build the base package each time you use splitChunks to extract it. The following basic package time can be omitted.
Improve the speed of secondary construction
Method 1: Use terser-webpack-plugin to enable caching
module.exports = {
optimization: {
minimizer: [
new TerserWebpackPlugin({
parallel: true.cache: true,}),],},};Copy the code
Method 2: Use cache-loader or hard-source-webpack-plugin
module.exports = {
plugins: [new HardSourceWebpackPlugin()],
};
Copy the code
Narrow your Build goals
For example, babel-loader does not resolve node_modules
module.exports = {
rules: {
test: /\.js$/,
loader: "happypack/loader".// exclude: "node_modules"
/ - or /
// include: path.resolve("src"),}}Copy the code
Reduce file search scope
- To optimize the
resolve.modules
Configuration (reduce module search hierarchy) - To optimize the
resolve.mainFields
configuration- To find the first
package.json
In themain
The file specified in the field -> find the root directoryindex.js
– > searchlib/index.js
- To find the first
- To optimize the
resolve.extensions
configuration- Module path lookup,
import xx from "index"
Will look for the.js
The suffix
- Module path lookup,
- The rational use of
alias
module.exports = {
resolve: {
alias: {
react: path.resolve(__dirname, "./node_modules/react/dist/react.min.js"),},modules: [path.resolve(__dirname, "node_modules")].// Find dependencies
extensions: [".js"].// Find the module path
mainFields: ["main"].// find the entry file}};Copy the code