“This is the seventh day of my participation in the First Challenge 2022. For details: First Challenge 2022.”
🧨 Hi, I’m Smooth, a sophomore SCAU front er
🏆 This article will get you started with Webpack and give you an easy-to-understand introduction to basic and advanced configurations!
🙌 if the article is wrong, please comment section correction, thank you!
Custom Loader content updated on 2022/02/25 Custom Plugin content updated on 2022/02/26
Webpack
This tutorial uses NPM to explain package management
Study background
The vUE cli and create-React-app scaffolding have already configured webPack parameters by default, so there is no additional need to configure webPack.
But in a project development, an optimized packaging suddenly for the items you want to increase the speed and volume of demand, so I began to learn the webpack, gradually understand the power of packaging tools, when looking at the scaffold to our default configured webpack configuration file, also understood that the scaffold such thing as a convenient place.
Of course, after the system learning webpack, you can also do to castrate scaffolding webpack configuration files do not use some configuration options, and add some configuration you need, such as optimization of packaging volume, improve packaging speed and so on.
This article will walk you through the basics of WebPack
PS: For the webpack configuration file, vue-cli can be modified by modifying vue.config.js. Create-react-app needs to be overwritten by Craco or exposed by eject.
Webpack introduction
webpack
What is the
Bundler: a module packaging tool
webpack
role
Package the project, specify project entry, file hierarchy, translate code (translate code into code that the browser knows, such as import/export)
webpack
Environment configuration
Webpack installation prerequisite: Node has been installed, use the commands node -v and NPM -v to test whether the node installation is successful
NPM install webpack webpack-cli --save-dev // --save-dev NPM install webpack webpack-cli -g // not recommended, end with -g, global installation (if two projects use two versions of webpack, there will be a conflict of versions) NPX webpack -v: NPM init -y: NPM init -y: NPM init -y: NPM init -y: NPM info webpack initializes the NPM repository with the -y suffix meaning that all options are set to yes by default when creating package.json files: NPM install webpack@ webpack-cli -d: install webpack NPX webpack; packageCopy the code
Webpack – CLI and Webpack differences:
Webpack-cli allows us to run webpack-related commands from the command line, such as webpack, NPX webpack, etc
webpack
The configuration file
The default configuration file is webpack.config.js
const path = require('path'); Module. Exports = {mode: "production", / / the environment, the default production or production environment, packaging the file compressed (can write), development not compressed entry: // exit filename: 'bundle.js', // exit filename: 'bundle.js', // exit filename: 'bundle.js', // exit filename: 'bundle.js' Path. resolve(__dirname, 'bundle'), // Export file to which folder to pack, argument (absolute path root, filename)}}Copy the code
If you want webPack to be packaged according to other configuration file rules, for example, called webPackConfig.js
npx webpack --config webpackconfig.js
Copy the code
Small problem:
Why use react/Vue frameworks to package project files instead of NPX webpack, type NPM start/ NPM run dev, etc?
Reason: Changed scripts script instructions in package.json file (this file: description of the project, including required dependencies, runnable scripts, project name, version number, etc.)
{"scripts": {"bundle": "webpack"}}Copy the code
Review:
NPX webpack index.js // After installing webpack locally (within the project), NPM run bundle -> webpack NPM run bundle -> webpack NPM run bundle -> webpackCopy the code
NPM Run Bundle replaces webpack
Basic concepts of Webpack
Concepts section of Webpack
The official documentation
Loader
What is Loader?
Webpack only knows and supports packing JS files by default. If you want to expand its ability to pack CSS files and image files, you need to install Loader to expand it
The use of the Loader
Do this in the webpack.config.js configuration file
New module field in the file, new rules array in the module, a series of rules configuration, each rule object has two fields
Test: matches the package of all files with the suffix XXX and uses the regular expression to match
Use: indicates the name of the loader to be used and to be installed
Name specifies the name of the packaged file.[name].[ext] indicates the name and suffix of the packaged file
{module: {rules: [{test: /.jpg$/, use: {loader: 'file-loader', options: {// placeholder placeholder name: '[name].[ext]' } } } ] } }Copy the code
Use the NPM install Loader name or YARN add Loader name in the root directory of the project to install the required loader
Common Loaders
Babel-loader, style-loader, CSS-loader, less-loader, sass-loader, postCSs-loader, URl-loader, file-loader, etc
Image (Images)
Pack image files
Image static resources, so they correspond to file-loader, and in general projects these static resources are placed in the images folder, using the use field to configure additional parameters
{ module: { rules: [ { test: /.(jpg|png|gif)$/, use: { loader: 'file-loader', options: { name: '[name].[ext]', outputPath: 'images/' // the folder path to which files with the suffix above are packaged}}}]}}Copy the code
Of course, for file-loader, url-loader is more extensible
It is recommended to use url-loader instead, because the limit parameter can be set. If the image size is larger than the corresponding byte size, it will be packed to the specified folder directory. If the image size is smaller, it will generate Base64 (it will not be packed to the folder, but to generate base64 into the OUTPUT JS file).
{ module: { rules: [ { test: /.(jpg|png|gif)$/, use: { loader: 'url-loader', options: { name: '[name].[ext]', outputPath: 'images/', // If matched to files with the suffix above, package to this folder path LIMIT: 2048 // specified size}}}]}}Copy the code
Style (CSS)
Package style file
Css-loader and style-loader need to be configured in the use field
Note: After setting the CSS style, mount it to the style property, so need two
{
module: {
rules: [
{
test: /.css$/,
use: ['style-loader', 'css-loader']
}
]
}
}
Copy the code
For SCSS files, configure the sass-Loader in use in addition to the above two loaders, and then install the two files
npm install sass-loader node-sass webpack --save-dev
Matters needing attention:
The loader array in use is packaged from right to left, top to bottom (SCSS), style first, then CSS, and sASS from right to left
use: ['style-loader', 'css-loader', 'sass-loader']
Copy the code
postcss.loader
Postcss. loader: postcss.loader: postcss.loader: postcss.loader: postcss.loader: postcss.loader: postcss.loader: postcss.loader Create a postcss.config.js file to configure the loader
module.exports = {
plugins: [
require('autoprefixer')
]
}
Copy the code
Style development
How do I get WebPack to recognize and package reimported LESS files within less files?
How to modularize export and use styles? (css in js)
{ module: { rules: [ { test: /.scss$/, use: ['style-loader', { loader: 'css-loader', options: { importLoaders: 2, // Allow less file modules: true // allow modular import/export using CSS in JS}}, 'sass-loader, 'postCSs-loader']}]}}Copy the code
Font (Fonts)
Package font files (with iconfont)
After downloading the font files of the corresponding ICONS from the iconfont website and compressing them to the directory, it is found that the downloaded iconfont. CSS file also introduces eOT, TTF, and SVG files, which cannot be recognized by Webpack. Therefore, you need to configure packaging rules for the files with these three suffivities. Use file – loader
{ module: { rules: [ { test: /.scss$/, use: ['style-loader', { loader: 'css-loader', options: { importLoaders: 2, // Allow less modules to be added to less files: True / / modular import and export permission to use CSS, CSS in js similarly}}, 'sass - loade,' postcss - loader ']}, {/ / configure the rules to the test: /. (eot | the vera.ttf | SVG) $/, use: { loader: 'file-loader' } } ] } }Copy the code
Custom Loader
First and foremost, writing a Loader is simply writing a function and exposing it to Webpack
For example, write a replaceLoader that replaces one character with another when encountered, such as hi when encountered with a Hello string
// Replaceloader. js in the loaders folder of the root directory: './loaders/replaceLoader.js' module.exports = function(source) { return source.replace('hello', 'hi'); }Copy the code
A simple Loader is now written
Pay attention to
The exposed function cannot be written as an arrow function, i.e., as follows:
// replaceLoader.js
module.exports = (source) => {
return source.replace('hello', 'hi');
}
Copy the code
Since the arrow function does not have this pointer, Webpack will make some changes when using Loader to bind some methods to this, so it will not be able to call some methods that belong to this.
For example, the parameters passed into the Loader are obtained through this.query
Of course, to use your custom Loader, in addition to the above write Loader, also need to use it, inWebpack
Configuration file For related configuration
// webpack.config.js const path = require('path'); module.exports = { mode: 'development', entry: { main: './src/index.js', }, module: { rules: [{ test: /.js/, use: [path. Resolve (__dirname, '. / loaders/replaceLoader. Js') / / here to write the js file path]}}], the output: {path: path.resolve(__dirname, 'dist'), filename: '[name].js' } }Copy the code
If you want to pass some parameters to your custom Loader, pass them as follows:
// webpack.config.js const path = require('path'); module.exports = { mode: 'development', entry: { main: './src/index.js', }, module: { rules: [{ test: /.js/, use: [{loader: path. Resolve (__dirname, '. / loaders/replaceLoader. Js'), / / here to write the js file path options: {name: 'hi' } } ] }] }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js' } }Copy the code
As a result, Webpack passes the {name: ‘hi’} argument to replaceloader.js when it is packaged
In replaceloader.js, the parameters are received as follows:
// replaceLoader.js
module.exports = (source) => {
return source.replace('hello', this.query.name);
}
Copy the code
In this way, the Hello string in all js files of the original project is replaced with hi
This completes a simple Loader
More and more
loader-utils
However, sometimes it is weird to pass parameters to a custom Loader. For example, the passed object may become only a string. In this case, the loader-utils module is used to analyze the passed parameters and parse them into correct contents
Method of use
First run NPM install loader-utils –save-dev install and then
// replaceLoader.js const loaderUtils = require('loader-utils'); Module.exports = function(source) {const options = loaderutils.getoptions (this); // Use return source.replace('hello', options.name); }Copy the code
callback()
Sometimes, in addition to making changes to the original project with a custom Loader, if sourceMap is enabled, you want the mapping of sourceMap to change as well.
Since this function only returns changes to the project content and not to sourceMap, some configuration is done with callback
this.callback( err: Error | null, content: string | Buffer, sourceMap? : SourceMap, meta? : any )Copy the code
Callbacks from this function can return sourceMap, error, meta changes in addition to project content changes
Since I only need to return the contents of the project and the sourceMap changes, an example configuration is as follows:
// replaceLoader.js const loaderUtils = require('loader-utils'); Module.exports = function(source) {const options = loaderutils.getoptions (this); // Use const result = source.replace('hello', options.name); this.callback(null, result, source); }Copy the code
async()
Sometimes there will be asynchronous operations in the custom Loader, such as setting the delay timer for 1s and then packing (convenient for fishing), so if you directly setTimeout(), setting a delay timer and then returning will certainly not work, error will be reported, because normally it is not allowed to return content in the delay timer.
We can do this with async() as follows:
// replaceLoader.js const loaderUtils = require('loader-utils'); Module.exports = function(source) {const options = loaderutils.getoptions (this); // Use const callback = this.async(); setTimeout(() => { const result = source.replace('hello', options.name); callback(null, result); Callback ()}, 1000); }Copy the code
Async () is similar to callback(), except that it is used for asynchronous returns
Customize multiple Loaders at the same time
For example, you want to implement a requirement that the packaged project first replaces all strings in the project with Hello and then with hi to Wow
Write two Loaders, the first to replace Hello with hi and the second to replace hi with Wow
The first replaceLoader. Js
// replaceLoader.js const loaderUtils = require('loader-utils'); Module.exports = function(source) {const options = loaderutils.getoptions (this); // Use const callback = this.async(); setTimeout(() => { const result = source.replace('hello', options.name); callback(null, result); Callback ()}, 1000); }Copy the code
The second replaceLoader2. Js
// replaceLoader2.js
module.exports = function(source) {
return source.replace('hi', 'wow');
}
Copy the code
Also configure webpack.config.js
// webpack.config.js const path = require('path'); module.exports = { mode: 'development', entry: { main: './src/index.js', }, module: { rules: [{ test: /.js/, use: [ { loader: path.resolve(__dirname, './loaders/replaceLoader2.js') }, { loader: Path. The resolve (__dirname, '. / loaders/replaceLoader. Js') / / here to write the js file path, the options: {name: 'hi'}},]}}], the output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js' } }Copy the code
Something to watch out for
The Loader executes from bottom to top and from right to left, so write the first one at the bottom and the second one at the top
Loader import is converted to the official import mode
In the above example, the loader is imported in the same way
loader: path.resolve(__dirname, './loaders/replaceLoader2.js')
Copy the code
Too long, too troublesome, not beautiful, want to change official introduction means, how should do
loader: 'replaceLoader2'
Copy the code
Configure the resolveLoader field in the Webpack configuration file
// webpack.config.js const path = require('path'); module.exports = { mode: 'development', entry: { main: './src/index.js', }, resolveLoader: { modules: ['node_modules', './loaders'] }, module: { rules: [{ test: /.js/, use: [ { loader: 'replaceLoader2' }, { loader: Options: {name: 'hi'}},]}]}, output: {path: path.resolve(__dirname, 'dist'), filename: '[name].js' } }Copy the code
If no Loader can be found in node_modules, the loaders folder of the same directory will be searched
More design considerations for Loader
Recommend some custom and practical loaders
- Global exception monitoring, idea: to all functions outside the envelope
try{} catch(err) {console.log(err)}
statements - style-loader
module.exports = function(source) {
const style = `
let style = document.createElement("style");
style.innerHTML = ${JSON.stringify(source)};
document.head.appendChild(style)
`
return style;
}
Copy the code
Plugins
-
Use plug-ins to make packaging faster and more diverse
-
At some point in the packaging lifecycle, plug-ins help you do something
Here are a few common plug-ins
html-webpack-plugin
Function: Because webpack does not generate index.html file by default, htmlWebpackPlugin automatically generates an HTML file after packaging, and automatically introduces the JS generated by packaging into this HTML file
Plug-in run life cycle: after packaging
Parameter: Object
new HtmlWebpackPlugin({
template: 'index.html'
})
Copy the code
clean-webpack-plugin
Function: Deletes all contents in a directory before packaging to prevent duplication
Plug-in run life cycle: before packaging
Parameter: Array format
[‘ Name of folder to delete ‘]
new HtmlWebpackPlugin(['dist'])
Copy the code
Customize a Plugin
First of all, at the most basic level, writing a plugin is simply writing a class and exposing it to Webpack to perform operations on it during a packaged lifecycle.
For example, write a copyright-webpack-plugin
Class CopyrightWebpackPlugin {constructor() {// copyright-webpack-plugin.js class CopyrightWebpackPlugin {constructor() { Console. log(' plugins used ')} apply(compiler) {}} module.exports = CopyrightWebpackPlugin;Copy the code
webpack.config.js
// webpack.config.js
const path = require('path');
const CopyRightWebpackPlugin = require('./plugins/copyright-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
main: './src/index.js',
},
plugins: [
new CopyRightWebpackPlugin()
],
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
}
}
Copy the code
A simple Plugin is now complete
More and more
Pass the parameter to the plugin
When you create the plug-in instance in the Webpack configuration file, you just pass in the parameters
plugins: [
new CopyRightWebpackPlugin({
name: 'Smoothzjc'
})
],
Copy the code
This way, the parameter is received in the constructor of the class
Class CopyrightWebpackPlugin {constructor(options) {console.log(' I am ', options.name) } apply(compiler) { } } module.exports = CopyrightWebpackPlugin;Copy the code
Different life cycle
As I mentioned earlier, plug-ins help you do things during the packaging lifecycle,
So we can write things that we want Webpack to do for us during the different life cycles of Webpack
Common life cycles:
emit
Asynchronous hooks that are ready to be packaged to the build directory, at the last moment when packaging is completecompile
Sync hook, ready for packing
Some arguments for the following example:
Compiler configuration, including packaging-related content
Compilation of all the contents of this packaging
If you want to add a file to the compilation directory before the compilation is complete, you can configure the Assets property of compilation
The relevant code runs inapply
Properties of the
Class CopyrightWebpackPlugin {constructor(options) {console.log(' I am ', Options. Name)} the apply (compiler) {/ compile/synchronization hook compiler.hooks.com running. Tap (' CopyrightWebpackPlugin ', () => {console.log(' compile hooks into effect '); }) / / asynchronous hook emit compiler. The hooks. Emit. TapAsync (' CopyrightWebpackPlugin '(compilation, Cb) => {compilation. Assets ['copyright. TXT '] = {compilation.assets['copyright. Funciton () {return 'copyright write by Smoothzjc'}, // The file size: Function () {return 28}} // Call cb(); }) } } module.exports = CopyrightWebpackPlugin;Copy the code
Debug as you write your plug-in
Most debugging tools are written based on Node. Here is an example: How to use the debugging tool to debug plugin when writing
- Add script instruction first, pass
node
Run the debug tool
// package.json
{
"scripts": {
"debug": node --inspect --inspect-brk node_modules/webpack/bin/webpack.js,
"build": "webpack"
}
}
Copy the code
- Break points where debugging is needed
Class CopyrightWebpackPlugin {constructor(options) {console.log(' I am ', options.name) } apply(compiler) { compiler.hooks.compile.tap('CopyrightWebpackPlugin', () => { console.log('compiler'); }) compiler.hooks.emit.tapAsync('CopyrightWebpackPlugin', (compilation, cb) => { debugger; // The compilation. Assets ['copyright.txt'] = {// The contents of the file are configured in the source property. This example means that the contents of the file are a function and the return value is source: Funciton () {return 'copyright write by Smoothzjc'}, // The file size: Function () {return 28}} // Call cb(); }) } } module.exports = CopyrightWebpackPlugin;Copy the code
-
The console runs the debug command NPM run debug
-
Open your browser and press F12 to open the console. You’ll see the Node icon in the upper left corner of the developer tools. Click to go to some of the pages that webPack passes through
- You can place the mouse over the properties of the variable you want to view
- Or on the right
Watch
Properties Enter the name of the property you want to view
Entry
Package the entry file, and specify the js file name generated after the package
Argument: string or an object. The default generated file name is main. The generated file name is’ entry file path ‘.
Entry: './ SRC /index.js' or entry: {main: './ SRC /index.js'} both above and below are equivalentCopy the code
It can also be packaged into multiple JS files, i.e., multi-entry
entry:{
main: './src/index.js',
sub: './src/index.js'
}
Copy the code
Output
Output JS file name
Parameters:
filename
Finally package out of the JS file name, can be directlybundle.js
Specify, or you can[name].js
The name specified by entry can also be used[hash].js
Hash value specified by entrychunkFilename
Name of the file imported asynchronously
path
The folder and path of the package
publicPath
Add a prefix to the SRC import path of the packaged JS file, usually used for CDN configuration
PublicPath,
Such as:
Index.html to introduce js plate code
<script type="text/javascript" src="main.js"></script>
Copy the code
If you want to put all the packed JS files on the CDN, reduce the packed volume (in this case, the JS files need not be placed in the packed folder), for example
<script type="text/javascript" src="http://cdn.com.cn/main.js"></script>
Copy the code
The configuration can be as follows
publicPath: 'http://cdn.com.cn'
Copy the code
Output Configuration Example
output: {
publicPath: 'http://cdn.com.cn',
filename: '[name].js',
chunkFilename: '[name].chunk.js',
path: path.resolve(__dirname, 'dist')
}
Copy the code
resolve
More expansion operations when modules are introduced
extensions
Suffix name lookup when a module is introducedalias
Configure an alias for the path
Resolve: {extensions: [' CSS ', 'JPG', 'js',' JSX '], alias: {' @ ', '/ SRC/pages / / when the input @, automatically replaced by/SRC/pages}}Copy the code
extensions
Introduce modules in a project as follows
import Child from './child'
Copy the code
JSX = child.css = child.css = child.css = child.css = child.css = child.css = child.css = child.css = child.css = child.css = child.css = child.css = child.css = child.css = child.css = child.css = child.css = child.css = child.css = child.css = child.css = child.css = child.css = child.css = child.css = child.css
alias
Configure an alias for the path
alias: {
'@': '/src/pages'
}
Copy the code
Explanation: When @ is typed, it is automatically replaced with/SRC /pages
Common scenario: When you frequently use the root path to refer to certain files, such as:
import a from '/src/pages/a.js';
import b from '/src/pages/b.js';
import c from '/src/pages/c.js';
import d from '/src/pages/d.js';
Copy the code
Writing/SRC /pages multiple times can be cumbersome. Using @ instead of/SRC /pages simplifies typing and improves development efficiency
Note:
resolve
You have to configure it properly, otherwise it degrades performance, because if you’re looking forchild.jsx
Following the above configuration, you need to go through the previous three unnecessary steps
SourceMap
If the packaged file has mapping enabled, he knows the code mapping between the packaged file and the pre-packaged source file
For example, we know that line 96 of the main.js file in dist actually corresponds to the first line of the index.js file in SRC
Usually not enabled, defaultnone
Close, because it will slow down the packing speed and increase the packing volume
parameter
devtool: 'none' // source-map' source-map' // source-map' inline-source-map' // source-map' inline-source-map' // source-map' inline-source-map' // Eval is the most efficient way to start mapping, but it is not comprehensiveCopy the code
Recommendation:
Devtool: ‘cheap-module-eval-source-map’
Devtool: ‘cheap-module-source-map’
You do not need to configure devtool in production environments. However, you can enable devtool to quickly locate errors. You are advised to use the preceding parameters
Mode: ‘development’ is the development environment
Mode: ‘production’ is the production environment
See the following table for more other parameters
SourceMap configuration example
devtool: 'cheap-module-source-map'
Copy the code
WebpackDevServer
Enabling a local Web server improves development efficiency
Webpack directive (usually just configure the second script)
2. Webpack-dev-server Starts a Web server and opens the corresponding directory resources. After the directory resources are modified and saved, it will be repackaged and the web page will be refreshed automaticallyCopy the code
Each project we download goes through two instructions (install dependency + package run). Here is an example of a React project generated by the create-React app scaffolding
npm install
npm run start
Copy the code
The second step, in fact, is to run WebpackDevServer, you will find that the start will directly open the browser, and every time you save it will automatically repackage and refresh the page.
Hidden feature of WebpackDevServer: packaged resources do not generate a dist folder. Instead, packaged resources are placed in the computer’s memory, which can greatly improve packaging speed
parameter
contentBase
Which directory to put the file on the Web server to openopen
Whether to open it at the same time as packingThe browserAccess the project corresponding preview URLport
The port numberproxy
Set the agent
WebpackDevServer configuration example
webpackDevServer: {
contentBase: './dist',
open: true,
port: 8080,
proxy: {
'api': 'xxxxx'
}
}
Copy the code
Expand the content
In fact, it is equivalent to writing a webpack-dev-server by hand, but the family official has helped us to write a good configuration items are complete one, I do not need to write, just take you to expand, understand the source code behind webpack-dev-server is how to match node implementation
Use Webpack in Node
Check out the Node.js API section of the official documentation
Use WebPack on the command line
Check the Command Line Interface section of the official documentation
Hot Module Replacement
Hot Module UpdateHMR
When the content changes, only the changed part changes, other loaded parts are not reloaded (e.g. CSS style changes, only the corresponding style changes, js does not change)
parameter
hot
Enable hot module updatehotOnly
When set to true, hot update is disabled regardless of whether it is enabledwebpackDevServer
Automatically refresh the browser after saving
Also configured to webpackDevServer
Example of HMR configuration
const webpack = require('webpack');
devServer: {
hot: true,
hotOnly: true
}
plugins: [
new webpack.HotModuleReplacementPlugin()
]
Copy the code
Above is to enable HMR, below is to use HMR
// Example: when number.js changes, the function import number from './number' is called; number(); if(module.hot) { module.hot.accept('./number', () => { number(); })}Copy the code
CSS csS-loader is written for you, Vue vuE-loader is written for you, react babel-loader is written for preset. (preset)
If you’re introducing obscure data files that don’t have HMR built in, you’ll need to write.
Use Babel for ES6 syntax
Babel-loader, @babel/preset-env, @babel/polyfill
- Configuration options for babel-loader can be written in separately
.babelrc
In the file - In addition to converting ES6 to ES5 is not enough, some older browsers need to inject Promise and array.map with additional code that needs to be introduced
@babel/polyfill
UseBuiltIns: 'usage' // For the used code, just convert to ES5 and pack it into dist targets folder: Chrome: '67' // Google Chrome version is larger than 67, ES6 can be directly compiled normally, so there is no need to do ES5 conversion}Copy the code
Example:
Module: {rules: [{test: /.js$/, exclude: /node_modules/, loader: "babel-loader", options: {// [["@babel/preset-env", {targets: {edge: '17', Firefox: '60', Chrome: '67', Safari: '11.1'}, useBuiltIns: [[" Babel /plugin-transform-runtime", {"corejs": {"corejs": {"corejs": {"corejs": { 2, "helpers": true, "regenerator": true, "useESModules": false }]] } } ] }Copy the code
If you want to use Babel in React
Implement the React framework code package
Download @ Babel/preset – react
. Babelrc file
{
presets: [
[
"babel/preset-env", {
targets: {
chrome: "67",
},
useBuiltIns: "usage"
}
],
"@babel/preset-react"
]
}
Copy the code
Advanced Concepts for Webpack
Webpack’s Guides section
The official documentation
Tree Shaking
Only code that has been introduced for use will be packaged, and code that has not been introduced for use will not be packaged (to reduce the size of the code). This feature is enabled by default after WebPack 2.0
- ES Module only supported (static import)
- Common JS not supported (dynamic import)
Import {} from "// ESM supports const XXX = require(") // Common JS does not support thisCopy the code
Tree Shaking is not open in Development by default
If your development environment is Tree Shaking every time you recompile the packaged code, it will cause lines of code to not match when debugging
If you want to open the development environment
// package.json { "sideEffects": For example, {"sideEffects": ["*.css"]} if you don't want to Tree Shaking CSS filesCopy the code
Differentiated packaging of Development and Production modes
In general, the WebPack configuration files for the two environments do not change, but they can be in different file forms if you must
Webpack.dev.js is a development environment.
Webpack.proud. Js is a production environment.
The packaging script also needs to be changed if the difference is made
// package.json {"scripts": {"dev-build": webpack --config webpack.dev.js, // relative path or "proud-build": Webpack --config webpack.proud. Js, // relative path}}Copy the code
Webpack and Code Splitting
Why code split?
If the user needs to load a large JS file, fully 2MB, then each time the user visits this page, the user must load 2MB resources page can be displayed normally, but there may be a lot of code blocks are the current page does not need to use, so the unused code is divided into other JS files. Loading only when needed and reloading only when the page changes can greatly speed up page loading.
This means that proper code splitting through configuration can make the file structure clearer and the project run faster. For example, if loDash library is used, it can be split up.
Here’s an example:
Suppose we now have main.js(2MB) with lodash.js(1MB) in it. When accessing the page for the first time, load main.js(2MB). When the service logic of the page changes, load 2MB content again. Due to the browser's parallelism mechanism, it is faster to render two 1MB files in parallel when first accessing a page than to render only one 2MB file. Second, when the page business logic changes, simply reload main.js(1MB).Copy the code
At the same time, if both files use a module, if both files write this module once, there will be duplication. At this time, if the public module is removed and the two files reference it separately, then the module writing will be reduced once (reducing the package size).
That is, reasonable segmentation of code can also speed up the first screen loading speed, speed up repackaging speed (package volume reduction)
Code segmentation: The popular explanation is to divide a lump of code into multiple JS files
Code Splitting itself can be done manually, for example by pulling out common components, but why does Webpack almost come with Code Splitting now?
The SplitChunksPlugin plug-in in WebPack makes code splitting very easy, which is also a strong competitive point of WebPack
// webpack.config.js
{
optimization: {
splitChunks: {
chunks: 'all'
}
}
}
Copy the code
To summarize
Webpack does Code Splitting in two ways
1. Configure the plug-inSplitChunksPlugin
// webpack.config.js {optimization: {splitChunks: {chunks: 'all'}}} ' 'import _ from 'lodash'; // Write the business codeCopy the code
2. Dynamically import data asynchronously
function getComponent() {
return import('lodash').then(({ default: _ }) => {
let element = document.createElement('div');
element.innerHTML = _.join(['zjc', 'handsome'], '-');
return element;
})
}
getComponent().then(element => {
document.body.appendChild(element);
})
Copy the code
Of course, to support the asynchronous dynamic introduction of a module, you need to download babel-plugin-dynamic-import-webpack first
then
// .babelrc
{
presets: [
[
"@babel/preset-env", {
targets: {
chrome: "67"
},
useBuiltIns: 'usage'
}
],
"@babel/preset-react"
],
plugins: ["dynamic-import-webpack"]
}
Copy the code
SplitChunksPlugin
This section explains the plug-in configuration parameters in detail
The reuseExistingChunk attribute in the cache group is important for reducing packet size: with true enabled, modules to be split into the group will not be cached if they have already been cached in a group
Configuration of the sample
module.exports = { //... Optimization: {splitChunks: {chunks: 'all', {chunks: 'all', {async: 'all',} MinRemainingSize: 0, minChunks: MaxAsyncRequests: 3, maxInitialRequests: 3, // How many js files can be split automaticNameDelimiter: '~', // 'true' // When true, the following group's filename attribute takes effect enforceSizeThreshold: 50000, // cacheGroups, which, when packaging synchronized code, go through the setup above, and then further into the groups below, dividing the code into the following qualifying groups: {vendors: {test: /[\/]node_modules[\/]/, // Only when the library in node_modules is imported, the code is split to vendor group priority: -10, // The higher the priority, the higher the priority. True, filename: 'vendors. Js ', // Vendors group code splits are all split into filename name: 'Vendors' // generate vendors. Chunk. js, which should be set to filename}, default: {minChunks: 2, priority: -20, reuseExistingChunk: },},},},},},};Copy the code
See the official documentation for more configuration items
Lazy Loading
Dynamically importing a module asynchronously, usually on a routing configuration page, and lazily loading the imported components
function getComponent() {
return import('lodash').then(({ default: _ }) => {
let element = document.createElement('div');
element.innerHTML = _.join(['zjc', 'handsome'], '-');
return element;
})
}
getComponent().then(element => {
document.body.appendChild(element);
})
Copy the code
Packaging analysis
Apply webPack’s official tool Bundle Analysis
Preloading, Prefetching
Preloading Lazy loading. A component is imported only when an event is performed. For example, a component is imported only when an element is clicked
When core functions and interactions on the main page are loaded, a component will be preloaded if the network is idle so that it can be opened when you fetch it at any moment
Implement preloading:
Webpack is accompanied by magic comments, and webpackPrefetch: XXX is appended to the imported path
document.addEventListener('click', () => {
import(/* webpackPrefetch: true */ './click.js').then((func) => {
func()
})
})
Copy the code
This is also webpack’s most recommended first-screen loading optimization method, asynchronous introduction + preloading
Code splitting of CSS files
The Code splitting mentioned above is for JS files, which means the Code splitting of JS files, and the packaged CSS is in JS files
If you want to split CSS files into code, you can use the MiniCssExtractPlugin, which relies on hot updates and can only run in an online packaging environment
// webpack.config.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
plugins: [ new MiniCssExtractPlugin() ],
module: {
rules: [
{
test: /.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
},
};
Copy the code
By default, all imported CSS files are merged into one CSS file
The OptimizeCSSAssetsPlugin plugin can be used if you want to compress the CSS files from the code and merge the repeated properties together
// webpack.config.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
optimization: {
minimizer: [
new OptimizeCSSAssetsPlugin({})
]
},
plugins: [ new MiniCssExtractPlugin() ],
module: {
rules: [
{
test: /.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
},
}
Copy the code
If you want multi-entry CSS files to be merged together, you also need to use code splitting
// webpack.config.js const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); module.exports = { optimization: { splitChunks: { cacheGroups: { styles: { name: 'styles', // wrap all of these into the styles. CSS file test: /.css$/, chunks: 'all', enforce: }}}, plugins: [new MiniCssExtractPlugin()], module: {rules:}}, plugins: [new MiniCssExtractPlugin()], module: {rules: [ { test: /.css$/, use: [MiniCssExtractPlugin.loader, "css-loader"], }, ], }, } }Copy the code
Webpack and the Browser Cache
When the browser loads a resource, it caches it locally. When the user accesses the page next time to load the resource, the browser can quickly load the resource based on the cached file. The browser does not know that the file has been changed and needs to render again until the file name is changed.
The role of the browser feature
You can speed up the loading, and when a part of the page changes, you can only re-render the changed part, so that partial rendering is done
Question: How do I ensure that only the file names of the changed files are changed each time I pack, and the file names of the unchanged files remain the same?
If the Output property in the Webpack configuration file is set to the following
output: {
filename: '[name].js',
chunkFilename: '[name].chunk.js'
}
Copy the code
If made a change, the project file and the file name unchanged, packaged filename unchanged, because the browser has to load the file, cache the file name, when the file is changed and the file name you don’t change, the browser will not render again, every time in order to make the changed file name change, you can use the hash value
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].chunk.js'
}
Copy the code
Have WebPack create a separate hash based on the contents of the file and change the file name because the hash value changes when the contents of the file change
expand
In older versions of WebPack (webPack 4.0 and below), if you find that the hash value of files changes every time you repack them, even if they haven’t changed, you can solve this problem by configuring runtimeChunk
Optimization: {runtimeChunk: {name: 'runtime'}} // A runtime.hash.js file is generatedCopy the code
RuntimeChunk principle
Suppose I have two JS files packaged by Webpack. File A is the code related to business logic, and file B is the library code for code segmentation (such as Lodash). Since there are operations to introduce into the library in business logic, they will be related. The code associated with this association exists in both A and B files (commonly known as manifest), and the relationship between packages and js files and the nested relationship between JS and JS files changes slightly each time the MANIFEST is packaged, so even if the A and B files are not changed, The hash that’s packaged still changes.
By configuring runtimeChunk, you can separate the manifest-related code from the runtimeChunk, so that every time you repackage, only runtimeChunk changes. A file only has business logic, B file only has library files. There will be no manifest code in either A or B files, so the A and B files will not change, so the hash will not change
Shimming
Package compatible, automatic introduction
When you use a library on your page but don’t import it, the webPack code will help you import it automatically and “stealthily”
Plugins: [new webpack.ProvidePlugin({$: 'jquery', // When using $, jquery library is automatically introduced in that page _: _join: ['lodash', 'join'] // When _join is entered, the lodash library join method is introduced})]Copy the code
More and more
Use of environment variables
For development and production environments, there may be times when you really need to write separate WebPack profiles for configuration
But if you write it separately, there are a lot of attributes that are repetitive, and you don’t want to write more, what do you do?
For example, file A and file B are different configuration parameters in two environments, and file C is A common configuration file. In this case, you need to follow the A+C packing rules in the development environment and B+C packing rules in the production environment
Can be achieved bywebpack-merge
Configure different configuration files for development and production environments
// webpack.common.js const merge = require('webpack-merge'); const devConfig = require('./webpack.dev.js'); Const prodConfig = require('./webpack.prod.js'); } module.exports = (env) => {// If env is available, run the following command: If (env && env.production) {return merge(commonConfig, prodConfig); } else {return merge(commonConfig, devConfig); }}Copy the code
Modify the configuration in package.json file
{ scripts: { "dev": "webpack-dev-server --config webpack.common.js", "build": "Webpack --env.production --config webpack.common.js" --env.production --config webpack.common.jsCopy the code
Of course, during script configuration, you can also pass parameters to the configuration file as follows:
"build": "webpack --env production --config webpack.common.js"
Copy the code
At the same time, change the parameter to webpack.common.js
Module. exports = (env, production) => { If (env && production) {return merge(commonConfig, prodConfig); } else {return merge(commonConfig, devConfig); }}Copy the code
🎁 thank you for reading this article, I hope you can help, if you have any questions welcome to point out.
🎁 I’m Smoothzjc, please like it if you think it’s all right ❤
🎁 I will also work hard to produce more good articles in the future.
🎁 interested partners can also pay attention to my public account: Smooth front-end growth record, public account synchronous update
Writing is not easy, thanks for your support ❤
Phase to recommend
React Hook: How to Learn React Hook in 2022
Github + Hexo Implement your own personal blog, Configuration theme (super detailed)
10 minutes to thoroughly Understand how to configure subdomains to deploy multiple projects
This article explains how to configure pseudo static to solve the 404 problem of deployment project refresh page
Learn common horizontal and vertical Centered interview questions in 3 minutes
React: Emoji input with Antd+EMOJIALL
【 Suggested collection 】 a summary of git common instructions!! Suitable for small white and want to understand the basic instructions of Git at work.
A brief introduction to javascript prototypes and prototype chains