Write at the top
I have used webpack
a lot in my work recently. I want to study it systematically. I found a tutorial on Geek Time. I followed the video on the tutorial and learned a lot. Although the video tutorial was published in 2019, it is a bit old and some knowledge is outdated, but it does not hinder learning. Those outdated knowledge should be understood, and you can search for alternatives. Learning resources: play with webpack , the following are my study notes.
Build tool
Front-end technology development, such as: React
of jsx
, Vue
and Angular
instructions, CSS
pre-processor, the latest ES
grammar, the browser does not recognize.
The role of the build tool is to convert the grammar (high-level grammar) that the browser can't recognize into the grammar (low-level grammar) that the browser can recognize.
Another function is to compress and obfuscate the code, which reduces the size of the code while making the code difficult to read.
webpack
is a popular front-end construction tool.
webpack
acquaintance 061374642a0e86
Configure the script
script
After installing webpack
webpack
in the command line, there are two ways:
- Specify the path
./node_modules/.bin/webpack
- Use
npx
tool,npx webpack
These two methods are very troublesome to use. There is a simple way to use package.json
in scripts
, it can read the node_modules/.bin
directory 061374642a0f59.
npm run build
in the command line.
"scripts": {
"build": "webpack"
}
Configuration file
webpack
default configuration file webpack.config.js
, by webpack --config
to assign a profile.
For example, the configuration file of the production environment is webpack.config.prod.js
, and the configuration file of the development environment is webpack.config.dev.js
.
"scripts": {
"build": "webpack --config webpack.config.prod.js",
"build:dev": "webpack --config webpack.config.dev.js"
}
Core idea
webpack
has 5 core concepts: entry
, output
, mode
, loaders
, plugins
.
entry
The entry file when packaging, the default is ./src/index.js
entry
: single entrance and multiple entrances.
Single entry is used for single page applications, entry
is a string
module.exports = {
entry: "./src/index.js",
};
Multi-entry is used for multi-page applications, entry
is the object form
module.exports = {
entry: {
index: "./src/index.js",
app: "./src/app.js",
},
};
output
The packaged output file, the default is ./dist/main.js
.
entry
is a single entry, output
can modify the parameters path
and filename
.
const path = require("path");
module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "index.js",
},
};
entry
is a multi-entry, output
of filename
needs to use [name]
placeholder to specify the packaged name, corresponding to entry
in key
const path = require("path");
module.exports = {
entry: {
index: "./src/index.js",
app: "./src/app.js",
},
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].js",
},
};
mode
It can be set to development
, production
, none
, and the default is production
.
development
: development environment, setprocess.env.NODE_ENV
valuedevelopment
. Turn onNamedChunksPlugin
andNamedModulesPlugin
production
: production, providedprocess.env.NODE_ENV
valueproduction
. OpenFlagDependencyUsagePlugin
,FlagIncludedChunksPlugin
,ModuleConcatenationPlugin
,NoEmitonErrorsPlugin
,OccurrenceOrderPlugin
,SideEffectsFlagPlugin
andTerserPlugin
none
: Do not open any optimization options
ps:development
mode, the console will print out which module has hot update, and what is the path of this module. The plug-in opened inproduction
loaders
webpack
only supports the two file types js
and json
loader
is to process other files and add them to the dependency graph.
loader
is a function that receives the source file as a parameter and returns the converted result.
Generally, loader
named with -loader
as the suffix, such as css-loader
, babel-loader
.
test
is the specified matching rule, use
is the name specified to use loader
module.exports = {
module: {
rules: [
{
test: /.less$/,
use: ["style-loader", "css-loader", "less-loader"],
},
],
},
};
A loader
Usually only one thing, parsing less
when needed less-loader
to less
turn into css
, since webpack
not recognize css
, need css-loader
to css
converted commonjs
objects into js
, and finally need style-loader
to css
Insert it into style
on the page.
ps:loader
usually has two ways, one is from left to right (similar tounix
ofpipe
), and the other is from right to left (compose
).webpack
selectscompose
loader
once from right to left
plugins
Anything that loader
can't do can be plugin
. It is mainly used for file optimization, resource management, environment variable injection, and it affects the entire construction process.
Generally, plugin
named with -webpack-plugin
, and some of it -plugin
a suffix of 061374642a1392.
const CleanWebpackPlugin = require("clean-webpack-plugin");
module.exports = {
plugins: [new CleanWebpackPlugin()],
};
webpack
resource analysis
Analyze es6
Need to install @babel/core
, @babel/preset-env
, babel-loader
.babelrc
under the root directory, add @babel/preset-env
to presets
{
"presets": ["@babel/preset-env"]
}
webpack
is configured in babel-loader
module.exports = {
module: {
rules: [
{
test: /.js$/,
use: "babel-loader",
},
],
},
};
There is also a way of configuration, do not use .babelrc
file, configure it in use
parameters options
in
module.exports = {
module: {
rules: [
{
test: /.js$/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
},
},
},
],
},
};
Resolution css
Need to install css-loader
and style-loader
.
module.exports = {
moudle: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
],
},
};
Analyze less
Need to install less-loader
, css-loader
, style-loader
module.exports = {
module: {
rules: [
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader"],
},
],
},
};
Parse pictures and fonts
Need to install file-loader
module.exports = {
module: {
rules: [
{
test: /.(png|jpeg|jpg|gif)$/,
use: ["file-loader"],
},
{
test: /.(woff|woff2|eot|ttf|otf)$/,
use: ["file-loader"],
},
],
},
};
url-loader
url-loader
can convert smaller pictures into base64
.
module.exports = {
module: {
rules: [
{
test: /.(png|jpeg|jpg|gif)$/,
use: {
loader: "url-loader",
options: {
limit: 10240, // 小于 10k 图片,webpack 在打包时自动转成 base64
},
},
},
],
},
};
Configuration vue
Configure vue
development environment, need to install vue
, vue-loader
, vue-template-compiler
const { VueLoaderPlugin } = require("vue-loader");
module.export = {
plugins: [new VueLoaderPlugin()],
module: {
rules: [
{
test: /\.vue$/,
use: "vue-loader",
},
],
},
};
ps: Thevue-loader
used here is the15.x
version. I have a problem installing the latest version16.x
, which has not been resolved.
Configuration react
To configure the react
development environment, you need to install react
, react-dom
, @babel/preset-react
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env", "@babel/preset-react"],
},
},
},
],
},
};
webpack
File monitoring and hot update
File monitoring
Every time you modify the code, you need to build it manually, which affects development efficiency. webpack
provides the role of file monitoring. When the monitoring is turned on, webpack
will call the node
module in fs
to determine whether the file has changed. If there is a change, it will automatically rebuild and output a new file.
webpack
There are two ways to open the monitoring mode: (requires manual refresh of the browser)
- When starting the
webpack
command, bring the--watch
parameter
"scripts": {
"watch": "webpack --watch"
}
webpack.config.js
is set inwatch: true
module.exports = {
watch: true,
};
File monitoring and analysis
webpack
File monitoring is based on whether the last editing time of the file has changed.
It will save the modification time, and when the file is modified, it will be compared with the last modification time.
If it finds inconsistencies, it will not tell the listener immediately, but cache the file modification first, and wait for a period of time. If there are other files that change during this period, it will list the files in this period of time. Build together. The waiting time is aggregateTimeout
.
module.exports = {
watch: true,
watchOptions: {
ignored: /node_modules/,
aggregateTimeout: 300,
poll: 1000,
}
}
watch
: The default isfalse
watchOptions
: Only whenwatch
istrue
, it will take effectignored
: The files or folders that need to be ignored for monitoring, the default is empty, and thenode——modules
will be improved.aggregateTimeout
: The waiting time after the file change is monitored, the default is300ms
poll
: polling time,1s
once
Hot update
Hot update requires two plug-ins, webpack-dev-server
and HotModuleReplacementPlugin
Compared with watch
, it does not output files, directly in the memory, so its construction is faster.
Hot update will only be 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,
},
};
Configure commands in package.json
webpack4.x
"scripts": {
"dev": "webpack-dev-server --open"
}
webpack5.x
"scripts": {
"dev": "webpack server"
}
PS:webpack5.x
andwebpack-dev-server
there is a conflict, you can not use--open
open the browser.
HotModuleReplacementPlugin
The core of the hot update is HMR Server
and HMR Runtime
.
HMR Server
: is the server side, used to notify the browser side of thejs
module throughwebsocket
HMR Runtime
: is the browser side, used to receiveHMR Server
, the browser side can see the.hot-update.json
file
hot-module-replacement-plugin
effects: webpack
itself is constructed out of bundle.js
itself is not provided with heat update, HotModuleReplacementPlugin
effect is HMR Runtime
injected into bundle.js
, such bundle.js
can HMR Server
establish websocket
communication capabilities. Once the disk where the file is modified, then HMR Server
will have to modify js
module websocket
sent to HMR Runtime
, then HMR Runtime
to update the local code page, this method does not refresh your browser.
webpack-dev-server
role: providing bundle server
capacity is generated bundle.js
by localhost://xxx
to access methods are also provided at livereload
(auto-refresh browser).
File fingerprint
What is file fingerprinting
File fingerprint refers to the suffix of the file name output after packaging. For example: index_56d51795.js
.
Usually used for version management
File fingerprint type
hash
: Related to the construction of the project, as long as the project file changes, thehash
constructed project will change. If you usehash
calculate, the hash value after each build is different. If the content of the file does not change, then it is impossible to achieve caching.chunkhash
: It is related towebpack
packaged bychunk
. Differententry
will generate differentchunkhash
. The production environment will separate some public libraries from the source code andchunkhash
separately. As long as the code of the public library is not changed, its hash value will not change, and caching can be implemented.contenthash
hash
according to the content of the file. If the content of the file does not change, thencontenthash
does not change. Generally used forcss
resources, ifcss
resource usechunkhash
, then modify thejs
,css
will change the resources, the cache will fail, socss
usecontenthash
.
ps:
- Use
hash
scenes to combinemode
to consider, ifmode
isdevelopment
, usingHMR
case, usechunkhash
is not suitable, you should usehash
. Whenmode
isproduction
chunkhash
should be used.js
useschunkhash
find resources easily, becausejs
a higher degree of resource relevance.css
usescontenthash
becausecss
generally written based on different pages,css
resources is not high, so there is no need to updatecss
when other resources are modified.
js
file fingerprint
Set output
of filename
, use [chunkhash]
,
const path = require("path");
module.exports = {
output: {
path: path.join(__dirname, "dist"),
filename: "[name]_[chunkhash:8].js", // 取 hash 的前 8位
},
};
css
file fingerprint
Need to install mini-css-extract-plugin
style-loader
inserts css
into head
of the mini-css-extract-plugin
is extracted as a separate file, and 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",
}),
],
};
Image file fingerprint
Need to install file-loader
Placeholder name | meaning |
---|---|
[ext] | Resource suffix |
[name] | file name |
[path] | Relative file path |
[folder] | The folder where the file is located |
[contenthash] | hash file content, the default is md5 |
[hash] | hash file content, the default is md5 |
[emoji] | A random reference to the content of the file emoji |
Pictures of hash
and css/js
of hash
concept is not the same, the picture hash
there is a picture of the contents of the decision.
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]",
},
},
],
},
],
},
};
Code compression
Code compression is mainly divided into js
compression, css
compression, and html
compression.
js
compression
webpack
built-in uglifyjs-webpack-plugin
plug-in. By default, the files after packaging are compressed, and no additional configuration is required here.
You can install this plug-in manually, and you can set additional configurations. For example, to enable parallel compression, you need to set parallel
to true
const UglifyjsWebpackPlugin = require("uglifyjs-webpack-plugin");
module.exports = {
optimization: {
minimizer: [
new UglifyjsWebpackPlugin({
parallel: true,
}),
],
},
};
css
compression
Install optimize-css-webpack-plugin
css
preprocessor cssnano
at the same time.
const OptimizeCssWebpackPlugin = require("optimize-css-webpack-plugin")
module.exports = {
plugins: [
new OptimizeCssWebpackPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require("cssnano)
})
]
}
html
compression
Need to install html-webpack-plugin
, by setting compression parameters.
ps: Multi-page applications need to write multiple new HtmlWebpackPlugin({...})
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, "src/search.html"), //html 模版所在的位置,模版里面可以使用 ejs 的语法
filename: "search.html", // 打包出来后的 html 名称
chunks: ["search"], // 打包出来后的 html 要使用那些 chunk
inject: true, // 设置为 true,打包后的 js css 会自动注入到 HTML 中
minify: {
html5: true,
collapseWhitespace: true,
preserveLineBreaks: false,
minifyCSS: true,
minifyJS: true,
removeComments: false
}),
new HtmlWebpackPlugin({...})
],
};
The minifyJS
and minifyCSS
HtmlWebpackPlugin
parameter minify
js
ES6
syntax cannot be used) and css
which are inlined in the html
at the beginning.
chunks
corresponds to entry
in key
. chunk
you want to be injected automatically, write chunk
chunks
.
chunk
difference between 061374642a1fc9, bundle
, module
chunk
: Eachchunk
ismodule
, which can be divided into multiplechunk
bundle
: The final file generated by packagingmodule
:webpack
injs
,css
, pictures)
Automatically clean up the build directory
Every time during the build, a new file will be generated, resulting in more and more output
The most common cleaning method is rm
. Before packaging, execute the rm -rf
command to dist
directory, and then pack it.
"scripts": {
"build": "rm -rf ./dist && webpack"
}
The other is to use rimraf
to delete.
To install rimraf
, first dist
directory before packaging, and then package it.
"scripts": {
"build": "rimraf ./dist && webpack"
}
Although these two solutions can dist
directory, they are not very elegant.
webpack
provides clean-webpack-plugin
, which will automatically clean up output.path
under 061374642a2133.
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
plugins: [new CleanWebpackPlugin()],
};
Auto-fill style prefix
Different browser manufacturers have not fully unified the standards for css
display: flex
, which should be written as display: -webkit-box
in the webkit
kernel.
Adding the kernel one by one during development will be a huge project.
In webpack
, you can use loader
to solve the problem of automatically css
prefix of 061374642a219c.
Install postcss-loader
and its plug-in autoprefixer
.
autoprefixer
is based can i use
This site provides css
be incomplete prefix compatibility.
autoprefixer
is a post processor, which is different from the preprocessor. The preprocessor is processed during packaging, while autoprefixer
is processed after the code has been processed and the style has been generated.
In webpack4.x
installation postcss-loader@3.0.0
, autoprefixer@9.5.1
.
Method 1: directly configured 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"], //最新的两个版本,用户大于1%,且兼容到 ios7
}),
],
},
},
],
},
],
},
};
Method 2: uses the postcss
configuration file postcss.config.js
, and webpack.config.js
directly writes postcss-loader
.
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",
],
},
],
},
};
postcss.config.js
:
module.exports = {
plugins: [
require("autoprefixer")({
overrideBrowserslist: ["last 2 version", ">1%", "ios 7"],
}),
],
};
Method three: browser compatibility can write package.json
in, postcss.config.js
in just load autofixer
can be.
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",
],
},
],
},
};
postcss.config.js
:
module.exports = {
plugins: [require("autofixer")],
};
package.json
:
"browserslist": {
"last 2 version",
"> 1%",
"ios 7"
}
Resource inlining
Resource inlining needs to be used in some scenarios, the common ones are:
- The initialization script of the page frame
- Reduce
http
network requests, change some small pictures intobase64
content into the code, less requests css
inline to increase page experiencejs
executed as soon as possible, such as theREM
plan
html
inline
In a multi-page project, head
there are many common label, such as meta
, want to improve maintenance, it will extract into a template, and then reference room.
Install raw-loader@0.5.1
<head>
${require("raw-loader!./meta.html")}
</head>
js
inline
In REM
program, you need as soon as possible html
label font-size
, then this js
will load as soon as possible, to perform.
<head>
<script>
${require('raw-loader!babel-loader!./calc-root-fontsize.js')}
</script>
</head>
css
inline
In order to have a better experience and avoid page flickering, you need to inline the css
head
.
Install html-inline-css-webpack-plugin
, this plug-in needs to be placed behind 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(),
],
};
You need to css
into a separate file first.
ps: The difference between
style-laoder
andhtml-inline-css-webpack-plugin
style-loader
: Inserting the style is a dynamic process, the packaged source code will not have thestyle
tag, and thestyle
tag is dynamically insertedjs
html-inline-css-webpack-plugin
: Insertcss
into thestyle
tag of the page during construction
Multi-page application
Each page corresponds to a entry
, and at the same time corresponds to a plugins
in html-webpack-plugin
.
This method has a shortcoming, each additional entry
will increase a 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" }),
],
};
With the help of glob
a universal configuration can be realized.
Install glob
, and all pages must be placed under src
, and the entry file must be 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),
};
sourceMap
use 061374642a2655
The code released to the generation environment should be compressed and obfuscated, but after compression and obfuscation, reading the code is like reading a book.
sourceMap
provides the mapping relationship between the compressed code and the source code.
sourceMap
in the development environment, and closed in the online environment. When troubleshooting online, source map
to the error monitoring system.
source map
keywords
eval
: Useeval
package the module codesource map
: Generatemap
filecheap
: does not contain column informationinline
:.map
asDataURI
, without generating.map
files separatelymodule
: containsloader
ofsourcemap
source map
type
devtool | First build | Second build | Is it suitable for production environment | Targetable code |
---|---|---|---|---|
(none) | +++ | +++ | yes | The final output code |
eval | +++ | +++ | no | webpack generated code (a module) |
cheap-eval-source-map | + | ++ | no | The code after loader conversion (only lines can be seen) |
cheap-module-eval-source-map | o | ++ | no | Source code (only lines can be seen) |
eval-source-map | -- | + | no | Source code |
cheap-source-map | + | o | yes | ( loader conversion can only see the line) |
cheap-module-source-map | o | - | yes | Source code (only lines can be seen) |
inline-cheap-source-map | + | - | no | The code after loader conversion (only lines can be seen) |
inline-cheap-module-source-map | o | - | no | Source code (only lines can be seen) |
source-map | -- | -- | yes | Source code |
Inline-source-map | -- | -- | no | Source code |
hidden-source- map | -- | -- | yes | Source code |
Extract page public resources
In the project, some basic libraries are used in multiple pages, such as react
, react-dom
, and a common code. When packaging these will be packaged into the final code, which is wasteful, and after packaging The volume is also relatively large.
webpack4
built-in SplitChunksPlugin
plug-in.
chunks
parameter description:
async
Asynchronously imported libraries are separated (default)initial
Synchronously imported libraries are separatedall
All imported libraries are separated (recommended)
Pulled out the name of the base library cacheGroups
in the filename
into html-webpack-plugin
in chunks
in, it is automatically imported:
module.exports = {
plugins: [
new HtmlWebpackPlugin({
chunks: ["vendors", "commons", "index"], //打包出来后的 html 要使用那些 chunk
})
);
],
optimization: {
splitChunks: {
chunks: "async",
minSize: 30000, // 抽离的公共包最小的大小,单位字节
maxSize: 0, // 最大的大小
minChunks: 1, // 资源使用的次数(在多个页面使用到), 大于1, 最小使用次数
maxAsyncRequests: 5, // 并发请求的数量
maxInitialRequests: 3, // 入口文件做代码分割最多能分成3个js文件
automaticNameDelimiter: "~", // 文件生成时的连接符
automaticNameMaxLength: 30, // 自动自动命名最大长度
name: true, //让cacheGroups里设置的名字有效
cacheGroups: {
//当打包同步代码时,上面的参数生效
vendors: {
test: /[\\/]node_modules[\\/]/, //检测引入的库是否在node_modlues目录下的
priority: -10, //值越大,优先级越高.模块先打包到优先级高的组里
filename: "vendors.js", //把所有的库都打包到一个叫vendors.js的文件里
},
default: {
minChunks: 2, // 上面有
priority: -20, // 上面有
reuseExistingChunk: true, //如果一个模块已经被打包过了,那么再打包时就忽略这个上模块
},
},
},
},
};
tree shaking
(optimization for tree shaking)
There are multiple methods in a module, the methods used will be packaged into bundle
, and the methods not used will not be packaged.
tree shaking
is to pack only the used methods into bundle
, and the other unused ones will be uglify
stage.
webpack
supported by default, in .babelrc
set in modules: false
can. production
stage is enabled by default.
The syntax must be ES6
, and CJS
is not supported.
ps: IfES6
turn intoES5
, while also opentree shaking
, need.babelrc
set inmodule: false
, otherwisebabel
default willES6
turn intoCJS
written specification, so it can not betree shaking
.
DCE
(deal code elimination)
DEC
full name of deal code elimination, which means dead code elimination in Chinese. There are mainly the following three types:
- Code will not be executed
if (false) {
console.log("代码不会被执行");
}
- The result of code execution will not be used
function a() {
return "this is a";
}
let A = a();
- Code only writes but not reads
let a = 1;
a = 2;
tree shaking
principle
Perform static analysis on the code. In the compilation stage, whether the code is used or not is determined. You cannot analyze which code is used when the code is running. tree shaking
will mark the unused code with comments. These useless codes are erased in the uglify
Utilize the features of the ES6
- Can only appear as a statement at the top level of the module
import
can only be a string constantimport binding
isimmutable
Delete useless css
purgecss-webpack-plugin
: Traverse the code and identify thecss class
mini-css-extract-plugin
with 061374642a2d3c
uncss
:html
needjsdom
loaded by all stylespostCSS
resolved bydocument.querySelector
identified inhtml
nonexistent file selector which
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 }),
}),
],
};
scope hoisting
usage and principle analysis
Enough code will produce a large number of closures, as shown in the following figure:
When an external module is introduced, it will be wrapped in a function. When more modules are introduced, a large amount of function wrapping code will be generated, resulting in a larger volume. During operation, the more function scopes are created, the memory overhead will also increase.
- The module converted by
webpack
will be wrapped in one layer import
will be converted to__webpack_require
analyze
- What is packaged is a
IIFE
(anonymous closure) modules
is an array, each item is a module initialization function__webpack_require
used to load the module, returnmodule.exports
- Start the program through
WEBPACK_REQUIRE_METHOD(0)
scope hoisting
principle
Put the code of all modules in a function scope in the order of reference, and then appropriately rename some variables to prevent variable name conflicts.
Module calls are in order. The a
module calls the b
module. Because of the function package, a
module and the b
module does not matter. If the package code is eliminated, the modules need to be b
according to the module reference order, and the 061374642a2f2c module must be placed in a
Before the module, the b
module can be a
module.
Through scope hoisting
can reduce function declaration and memory overhead. production
stage is enabled by default.
Must be ES6
syntax, CJS
does not support.
ps:scope hoisting
turns multiple scopes into one scope. When the module is quoted more than1
times, it has no effect. If a module is cited more than1
times, then the code of this module will be inlined multiple times, thereby increasing the size of the packaged file.
Use ESLint
It is ESLint
that can unify the team's code style and help find errors.
Two methods of use:
- Integration with
CI/CD
- Integration with
webpack
webpack
and CI/CD
Integration
Need to install husky
.
Add scripts
and pass lint-staged
check and modify the file.
"scripts": {
"precommit": "lint-staged"
},
"lint-staged": {
"linters": {
"*.{js,less}": ["eslint --fix", "git add"]
}
}
webpack
and ESLint
Integration
Use eslint-loader
, the build is to check the js
specification
Install the plug-in babel-eslint
, eslint
, eslint-config-airbnb
, eslint-config-airbnb-base
, eslint-loader
, eslint-plugin-import
, eslint-plugin-jsx-ally
, eslint-plugin-react
.
Create a new file .eslintrc.js
module.exports = {
parser: "babel-eslint",
extends: "airbnb",
env: {
browser: true,
node: true,
},
rules: {
indent: ["error", 2],
},
};
webpack.config.js
File configuration eslint-loader
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: ["babel-loader", "eslint-loader"],
},
],
},
};
Code segmentation and dynamics import
For large web
applications, putting all the code in one file is obviously not effective enough, especially when some of your code blocks are used in some special circumstances.
webpack
has a function to divide your code into chunks
(chunks), and load them when the code runs to the point where they are needed.
scenes to be used:
- Extract the same code into a shared block
- Lazy loading of scripts makes the initial download code smaller
Lazy loading of js
script
CJS
:require.ensure
ES6
:import
(Currently there is no native support,bable
conversion is required)
Install @bable/plugin-syntax-dynamic-import
plugin
.babelrc
file:
{
plugins: ["@bable/plugin-syntax-dynamic-import"];
}
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}>点我</div>
<img src={img} alt="" />
</div>
);
}
}
One thing to note here, when cacheGroups
is minChunks
set to 1
, the lazy loading script set above will not take effect, because import
is statically analyzed when it is loaded.
cacheGroups: {
commons: {
name: "commons",
chunks: "all",
priority: -20, //值越大,优先级越高.模块先打包到优先级高的组里
minChunks: 1,
}
}
Multi-process/multi-instance: parallel compression
Method 1: Use webpack-parallel-uglify-plugin
plug-in
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,
},
},
}),
],
};
Method 2: Use uglifyjs-webapck-plugin
enable parallel
parameters
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
]
}
Method three: terser-webpack-plugin
enable parallel
parameters ( webpack4
recommended)
const TerserPlugin = require("terser-webpack-plugin");
module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
parallel: 4,
}),
],
},
};
Speed up the build
Idea: react
, react-dom
, redux
, react-redux
basic package and business basic package into one file
Methods: DLLPlugin
subcontracting, DLLReferencePlugin
of manifest.json
references
Use DLLPlugin
for subcontracting
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",
}),
],
};
Introduced at webpack.config.js
module.exports = {
plugins: [
new webapck.DllReferencePlugin({
manifest: require("./build/library/manifest.json"),
}),
],
};
In the project uses webpack4
, for dll
dependent not so big, use dll
relatively lift it is not particularly obvious, but hard-source-webpack-plugin
can greatly enhance the secondary building.
But from the actual front-end factory, dll
is still necessary. For a team, basically use the same technology stack, either react
or vue
. At this time, the usual practice is to type the public framework into a common bundle
file for all projects to use. dll
can well meet this scenario: multiple npm
packages are typed into a common package. dll
in the subcontracting plan in the team is still very valuable.
splitChunks
can also do DllPlugin
, but it is recommended to use splitChunks
to extract the public js
files between pages, because splitChunks
to extract the basic package, it still takes construction time. If it is DllPlugin
only needs to be pre-compiled once, and the subsequent basic package Time can be omitted.
Speed up the second build
Method 1: Use terser-webpack-plugin
open the cache
module.exports = {
optimization: {
minimizer: [
new TerserWebpackPlugin({
parallel: true,
cache: true,
}),
],
},
};
Method 2: Use cache-loader
or hard-source-webpack-plugin
module.exports = {
plugins: [new HardSourceWebpackPlugin()],
};
Reduce the build target
For example, babel-loader
does not resolve node_modules
module.exports = {
rules: {
test: /\.js$/,
loader: "happypack/loader",
// exclude: "node_modules"
/-- 或者 --/
// include: path.resolve("src"),
}
}
Reduce file search scope
- Optimize
resolve.modules
configuration (reduce module search level) Optimize
resolve.mainFields
configuration- First find
package.json
inmain
field specifies the file -> find the root directoryindex.js
-> Findlib/index.js
- First find
Optimize
resolve.extensions
configuration- To find the module path,
import xx from "index"
will first find the suffix of.js
- To find the module path,
- Reasonable use
alias
module.exports = {
resolve: {
alias: {
react: path.resolve(__dirname, "./node_modules/react/dist/react.min.js"),
},
modules: [path.resolve(__dirname, "node_modules")], // 查找依赖
extensions: [".js"], // 查找模块路径
mainFields: ["main"], // 查找入口文件
},
};
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。