Module packaging tool
Problems that need to be solved
1. New special new code compilation 2. Modular JS packaging 3
Module Bundler compilers and transforms the application through the Module Loader during the packaging process. Webpack also has the ability of Code Splitting, which means that the Code in the application is packaged according to the needs of users. We don’t have to worry about all the code packed together, creating a file that’s too big
We can pack together the files necessary for the initial page load and store the files of other modules separately. When we need them, we can load them asynchronously to achieve incremental or progressive loading. This way, we don’t have to worry about the extreme problem of the file being too broken or too big
Front-end module type problem, support in JS in a modular way, load arbitrary type of file
All of the packaging tools are aimed at modularity, and the modularity here is for the whole front-end project modularity, more macro than what we call JS modularity, so that we can enjoy the advantages of modules in the development stage, and do not have to worry about the impact of modularity on production
webpack
Quick start of Webpack
npm init -y
Initialize onepackage.json
npm i webpack webpack-cli --dev
After the installation is complete, thepackage.json
thescripts
add"build":"webpack"
So that we can passnpm run build --version
Looking at the webPack version we downloaded, it is important to note that webpack4 is quite different from WebPack 5. The main focus here is the 4.x version- I can use it here
serve
Tools to start a front-end static service, view the packaged project, and then wenpm run build
Webpack4 and later support zero configuration mode, directly start packaging, the default entry issrc/index.js
The exit is in the root directorydist/main.js
Most of the time, we need to customize the entry configuration. In this case, we need to add a special configuration file for Webpack. This method is to add a webpack.config.js file in the root directory of the project. So we need to write the code the commonJS way
const path = require('path')
/ * * *@type {import('webpack').Configuration}* /
module.exports = { // This file exports an object with properties to configure the Webpack option
entry: './src/main.js'.// Since it is a relative path, the preceding './ 'cannot be omitted
output: { // Sets the location of the output file. The value is an object
filename: 'bundle.js'.//filename can specify the output filename,
path: path.join(__dirname,'output') The path property must be an absolute path to the output file. You can obtain the complete absolute path of the output file from node's path module}}Copy the code
Webpack4 adds a new usage of working mode, which greatly simplifies the complexity of webpack configuration. During the previous packaging process, a configuration warning was printed in the command line terminal, as shown in the following figure
No mode property is configured. Mode has three configurable properties.
- When no one is configured
mode
Property, at this timewebpack
Will default toproduction
To work in this modewebpack
Some optimization plug-ins, such as code compression, are enabled by default development
Development mode, will automatically optimize the packaging speed, will add some debugging success of some assistance to the code, command line start method isnpm run build --mode develpoment
, you can also directly configure topackage.json
thescript
In thenone
Mode, which runs raw packaging without doing any extra processing
The specific differences between the three modes can be found in the official documentation
In addition to specifying our working mode through cli parameters, we can also configure our working mode in the WebPack configuration file, so that WebPack will perform the tasks according to our configured working mode
Module.exports = {// This file exports an object that can be configured with the webpack option mode:'development', // working mode, There are three types of entries: 'development'/' none '/' production ': './ SRC /main.js' // Filename: 'bundle.js', //filename can specify the name of the output file, path: Path. join(__dirname,'output') // Specify the directory where the output file resides. The value of the path attribute must be an absolute path.Copy the code
Webpack results run principle
The whole generated code is an immediate function, which is the working entry to WebPack. It takes an argument to Modules, and when it is called, it passes in an array. Each element in the array is a parameter list of the same function, and each function corresponds to a module in the source code. Each module is wrapped in such a function to achieve the module’s private scope
Load the resource module
Webpack only processes JS files by default. When other types of files need to be processed, different loaders are required. Loaders are the core of webpack resource loading, and different types of loaders can load any type of resources
The entry of webpack can be various types of files, but generally we still use JS files as the entry of packaging, because the entry of Webpack can be regarded as the entry of running our application to some extent. At present, front-end business is driven by JS.
In traditional code we need to separate CSS and JS, maintain them separately, and import them separately. Webpack suggests that we load CSS in JS. Why is that
Webpack not only advises us to introduce CSS in JS, but also advises us to introduce any resource files required by the current code in the process of writing code. What really needs resources is not the application, but the code. The code must load corresponding resources to work properly, which is the philosophy of WebPack
Through JS code to introduce resource files or establish resource files and JS dependency, there is a very obvious advantage, on-demand loading, all resource files will not be missing is necessary, JS drives the whole front-end application, the benefits of establishing this dependency is
Js does need these resource files to make sure they are not missing, both are necessary
Loader
File resource loader file-loader
Webpack met in packaging, image files, etc., according to our configuration in the configuration file is matched to the corresponding file loader, loader to meet such a file, first we import file, copied to the output directory, and then copy the output of the directory path as the return value of the return current module, so for our application, The required resource is published, and the access path of the resource can be obtained through the module’s export member.
Url-loaders represent files in the form of data urls, a special Url protocol that can be used to represent files directly. The traditional URL usually requires a corresponding file on the server, and we get this file by requesting this address. DataUrl is the way that the current URL can directly represent the content of the file. The text in this URL already contains the content of the file, and this URL will not send any type of HTTP request
Best practices
For small files in a project, urL-loader is used to convert small files into data urls to reduce the number of requests. Large files are stored in traditional file-loader format as a single file to improve the loading speed
The configuration is as follows
Common Loader classification
Compile and convert csS-loader file operation file-loader code check Class code check
Webpack handles es2015 WebPack because module packaging is required, so it handles import and export and some corresponding transformations, but it cannot handle other ES6 features. If it needs to handle these new features, We need to configure an additional compiled loader, the most common of which is babel-loader
The usage method is as follows:
npm i babel-loader @babel/core @babel/preset-env
Due to thebabel-loader
Need to rely onbabel
Core module of@babel/core
And a collection of plug-ins to perform specific feature transformations@babel/preset-env
Webpack is a packaging tool by default and does not handle new features in ES6 or later versions of the code. If we need to handle these new features, we need to configure a separate loader for the JS code to implement them
How webpack loads resources
How resources are loaded in js code
- follow
ESM
The standardimport
The statement- follow
CommonJS
The standardrequire
function
If you need to load an ESM using the require function, note that the default export of the ESM needs to be obtained by require(‘ XXXX ‘).default
- Search for AMD standard
define
Functions andrequire
function
Webpack is compatible with a wide range of modular standards, so don’t mix them in your project unless necessary, as this can significantly reduce the maintainability of your project
Standalone loaders load resources
It also handles some imported modules in the load resource
When the CSS-loader loads the CSS file, the @import directive and url functions in some attributes also trigger the loading of the corresponding resource module. The SRC attribute of the image label in the HTML file loaded by the HTml-loader also triggers the loading of the corresponding resource module
abouthtml-loader
Configuration problems of
The core working principle of Webpack is to find the entry of packaging according to the configuration, usually a JS file, and then according to the import and require statements of the entry file, parse the resource module that the file depends on, parse the corresponding dependency of the resource module, and finally parse the dependency tree of the dependency relationship between all files in the project. Webpack can recurse the dependency tree, find the corresponding resource file of each node, and then find the corresponding loader required by the corresponding resource file according to Modules/Rules, and finally put the loading result into bundle.js, so as to realize the packaging of the whole project.
The Loader mechanism is at the heart of WebPack
Write a loader by hand
The process of loading resources by Webapck is similar to the pipeline at work. The final result must be a JS code loader responsible for converting resource files from input to output. Multiple Loaders can be used for the same resource in turn
Each Loader needs to export a function. The whole function is the processing process of Loader for resources. The input is the content of the loaded resource file, and the final output (return) is a js code string
// Export by hand
const marked = require('marked')
module.exports = source= > {
console.log(source)
const html = marked(source)
// return `module.exports = ${JSON.stringify(html)}`
return `export default The ${JSON.stringify(html)}` // Both export methods are acceptable
}
Copy the code
Webpack plugin
Webpack’s plug-in mechanism enhances Webpack’s ability in project automation to solve the automation work in addition to resource loading in a project
Copy the zip code to clean the dist directory clean-webpack-plugin
Automatic generation of HTML html-webpack-plugin using packaged results
When going online, we need to publish the HTML file in the root directory and all the packing results in the dist directory at the same time, and confirm the reference path of JS files in the HTML file. Based on these two problems, htML-webpack-plugin is used to realize the automatic injection of packed JS files in the HTML file. There is no need to hardcode usage
npm i html-webpack-plugin --dev
New HtmlWebpackPlugin({template:'index.html', // specify template title: "webpack plugin sample", meta: { viewport:'width=device-width' } })Copy the code
The title here set the title of a need in the template HTML file template tags to add syntax < % = htmlWebpackPlugin. Options. The title % >, and the title attribute set when using HTML – loader, can’t take effect, There’s a little bit of caution here. HtmlWebpackPlugin. The options this property can be accessed to the plug-in configuration data, is a plug-in provided by internal variables.
If you need to output multiple page files at the same time, you can add multiple HtmlWebpackPlugin instance objects to your Plugins. Each object is responsible for generating one page file
plugins:[
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template:'index.html',
title: "webpack plugin sample",
meta: {
viewport:'width=device-width'
}
}),
new HtmlWebpackPlugin({
template:'index.html',
title: "webpack plugin",
filename:'about.html'
})
]
Copy the code
In the project, we also have some files that we don’t need to deal with. When packaging, we just need to copy directly to the target directory, copy-webpack-plugin. This function requires us to pass in an array to specify the path of the file that we want to copy
New CopyWebpackPlugin([// 'public/**', 'public' // this means that the contents of the public directory are copied to the output directory])Copy the code
Write a WebPack plug-in by hand
The plug-in mechanism of Webpack is a common hook mechanism in the process of software development. There are many links in the working process of Webpack, and Webpack has hooks buried in each link. When developing plug-ins, different tasks are mounted on different nodes
Wepack requires that the plug-in must be a function or an object that contains the Apply method. Generally, we define a plug-in as a class, define the Apply method in the class, and build an instance from the class to use it
Plug-in is to declare the cycle to webpack hook function to mount the task function to achieve, if you need to in-depth understanding of the plug-in mechanism, we need to in-depth understanding of the underlying implementation principle of Webpack, need to read the source code to understand
The source code
// Implement a plugin class myPluguin {apply(Compiler) {// This method is automatically called when Webpack starts // Compiler parameter // console.log('myPlugin starts ',compiler) // Continue with compiler.hooks. Tap ('myPluguin',compilation => {// Register hook functions through the TAP method, which takes two arguments, one is the name of the plug-in, The second is the function // compilation that needs to be mounted on this hook. For (const name in compiler.assets) {// Compiler.assets gets the information about the resource file to be written to // console.log('name',name); // console.log('name1',compilation. Assets [name].source()); If (name.endswith ('.js')){const contents = compilation.assets[name].source() const WithoutComments = contents. Replace (/\/\*\*+\*\/ g, ") // () => withoutComments, size: () => withoutComments.length } } } }) } }Copy the code
Enhance the WebPack development experience
Automatically compile watch working mode
When you start the webpack command, add a “watch” parameter to the command, so that the webpack will run in watch mode. After the package is finished, the webpack will not exit immediately, wait for the file to change, and automatically compile a new result “watch”: “Webpack –watch” we need to add this directive to the package.json script to use the file listening function
BrowserSync principle: WebPack automatically packages code into dist. Changes in Dist files are monitored by Browser-sync, which automatically refresh the dist.
webpack-dev-server
An HTTP Server for development is provided, and a number of development-friendly features such as auto-compile and auto-refresh browsers are integrated. 1. 'NPM I webpack-dev-server --dev', can be defined in 'NPM script' after installation, ==> '"start": "Webpack-dev-serve", run 'NPM run start' to start the service, we can also add '--open' parameter, 2. 'webpack-dev-server' will automatically use Webpack to package our application, and the packaging results are temporarily stored in memory **, which will greatly reduce unnecessary IO operations and greatly improve our build efficiency. 3. By default, 'webpack-dev-server' will use the build output file as the resource file for the build service. As long as the normal output file is packaged by Webpack, it can be accessed normally, but some static resources need to be accessed as resources of the development server. DevServer :{contentBase:['./public'] // specifies an additional way to specify an additional resource path}, ' 'Copy the code
In daily development, due to the same origin policy, we often need local cross-domain access interface address. Here we introduce the configuration of proxy service, as shown in the following figure
If you are interested in the concept of a host name as part of the HTTP protocol, learn about it yourself
Source Map
Build and compile to convert the source code to code that can be run in the production environment. There is a big difference between this code and the source code. In this case, we need to debug our application or generate error messages that cannot be located. The Source Map was created to address these issues, an alias Source Map that maps the relationship between the transformed code and the Source code
At present, many third-party libraries have a. Map file, which is actually a JSON file that records the mapping relationship before and after code conversion. As it is meaningless to production, the production environment does not need such a file
Version Version of the source map standard used by the current file Source name of the source file before conversion. Because multiple files may be merged into one file, So this is an array. The member names used in the names source code are base64-VLQ encoded strings that record the mappings before and after the transformation
We usually import such a file by adding a line of comment to the converted code, using the method //# sourceMappingURL=xxx.map
Role: to solve the front-end reference to build after compilation, resulting in the code and run the code is not the same, generated debugging problems
Source Map
configuration
When we configure source-map in webpack using the devtool property, the generated bundle.js file automatically introduces the annotations we mentioned earlier and the.map generated file
So far, WebPack supports 12 different ways to configure source-Map. Each way generates different source-Map effects and speeds. Good source-map effects are slow, and fast source-Map effects are poor
The different configuration modes are shown in the following figure
From the speed of initial construction, monitoring mode rebuild speed, whether suitable for production use, the quality after construction four dimensions of comparison
Eval mode in this mode, the code converted by each module is executed in the eval function. At the end of the string, the url path corresponding to the file is explained by sourceURL. Only the file can be located, and source-map file is not generated in this mode
With such a Webapck configuration, we can compare the differences between different configurations ourselves
const HtmlWebpackPlugin = require('html-webpack-plugin')
const allModes = [
'eval',
'cheap-eval-source-map',
'cheap-module-eval-source-map',
'eval-source-map',
'cheap-source-map',
'cheap-module-source-map',
'inline-cheap-source-map',
'inline-cheap-module-source-map',
'source-map',
'inline-source-map',
'hidden-source-map',
'nosource-source-map',
]
/**
* @type {import('webpack').Configuration}
*/
module.exports = allModes.map(item => {
return {
devtool: item,
mode: 'none',
entry: './src/main.js',
output: {
filename: `js/${item}.js`
},
module: {
rules: [
{
test:/\.js$/,
use: {
loader:'babel-loader',
options:{
presets: ['@babel/preset-env']
}
}
}
]
},
plugins:[
new HtmlWebpackPlugin({
filename:`${item}.html`
})
]
}
})
Copy the code
Conclusion:
- Eval – Whether to use
eval
Execute module code - cheap –
Source Map
Does it contain line information — yescheap
Contains no row information - Module – Whether it can be obtained
Loader
Deal with the previous source code — addmodule
You can get the source code before processing - Nosource – You can see where the error occurred during a throw, but not the source code, which is protected for production mode
- Hidden – Produced at build time
map
File, but the code is not introduced through comments, we can not see the effect in the development tool, once there is a problem, and then putmap
File import back - inline –
source-map
Mode,map
The file exists as a physical file, whileinline-source-map
In order tobase64
thedataUrl
Is embedded in the code, which makes the code very large,eval-source-map
It’s also in linesource-map
Embedded ones are generally the least usable because they make the code very large.
Select the appropriate Source map
The development environment individual chooses cheap-module-eval-source-map
- It is enough for the code to locate to the line
- Generally, the framework is used for development, and the code varies greatly before and after loader conversion. Debugging is generally to debug the source code
- While first packaging is slow, repackaging is fast, most of the time in monitoring mode
The producer chooses None or nosource-source-map
- Debugging is a development phase thing
- Preventing source code exposure
Choose no jedi, what suits you is the best
The HMR module is hot updated
When an application is running, a module in an application is replaced in real time. The running status of the application is not affected. Hot update Only the modified module is replaced to the application in real time, without completely refreshing the application
HMR is currently integrated with webpack-dev-server, we just need to run the webpack-dev-server command to add the parameter –hot, or add the configuration in the configuration file
The configuration needs to be divided into two steps
- We sometimes refresh the page when we modify js files because HMR is not as out-of-the-box as other modules
- Our style files can be hot updated in real time, because style-loader automatically handles hot updates of styles.
- When we use framework to modify JS, we do not do any processing, modify JS files also support hot update, because under framework development, every file is regular, framework is integrated within the scaffolding creation project HMR solution
Conclusion: We still need to manually deal with the hot replacement of JS module after hot update, different modules have different logic, different logic, processing process is also different
Matters needing attention:
- Error handling of HMR code will cause automatic refresh to find no error when we can in
devServer: { hot: true }
todevServer: { hotOnly: true }
HotOnly will only use HMR and will not fallback to live reloading. - If HMR is not enabled, the HMR API will report an error because it is used during the js hot update process
module.hot
Object is provided by HMR, not when HMR is not enabledmodule.hot
The solution to this object is to add a judgment to the outer layer - Much of the code that does not handle hot updates is removed during packaging
Optimization of production environment Production environment focuses on operating efficiency, development environment focuses on development efficiency, creating different configurations for different working environments
- Configuration files export different configurations based on different environments
- Each environment corresponds to a configuration file. Different environments have different configuration files
Large projects have different configuration files for different environments, while small projects can be determined in a single configuration file. For general configuration and production configuration, there is a webpack-Merge module that provides this function
Tree Shaking
It is automatically enabled in production mode, detects unreferenced code, and removes it. This is not a configuration option for WebPack, but a set of optimized features that are used together
The development mode configuration is as follows
Need to be inoptimization
PropertiesusedExports
andminimize
Property is true
We can optimize it even further
The normal result of packaging is to end up with each of our modules in a separate function. If we have many modules, then we will have many concatenateModules of module functions in the output: The true function is to combine all modules into one function as much as possible, which not only improves the operation efficiency, but also reduces the code volume. This feature is also called Scope reactorization
The premise for implementing Tree Shaking and Babel Tree Shaking is to use EMS to organize our code, and because webPack processes code that must be modular in the ESM way, when we deal with new JS features, Need to use Babel-loader’s preset-env, this plugin converts ESM code to CommonJS during conversion
Latest versionbabel-loader
It does not cause tree-shaking to fail because the ESM transformation plugin is automatically turned off. When the current environment supports EMS,babel-loader
Generates an identity,preset-env
The ESM conversion code is automatically disabled based on this identity
SideEffects side effects
A new feature in WebPack4 that allows you to configure ways to identify if your code has side effects provides Tree Shaking with larger compression controls
Side effects: The module does something other than export members when it executes
SideEffects is generally used to flag NPM module packages for sideEffects
SideEffects is the same as Tree Shaking. It is not the same as Tree Shaking
} // Package. Json "sideEffects": False // Indicates that none of the code in the current project has side effectsCopy the code
SideEffects needs to be set up in two places in the development environment, one to enable functionality and one to indicate that the current project code has no sideEffects
optimization: { sideEffects: When enabled, webpack checks for sideEffects in the package.json file of the project to which the current code belongs. If this module has no sideEffects, unused modules will not be packaged
Note: the only way to use this property is to make sure that your code really has no sideEffects, otherwise the code with sideEffects will be deleted by mistake when webpack is packaged. When the code with sideEffects needs to be saved, we can configure “sideEffects”: true, or
Optimization: {sideEffects: ['xx/xx/xx.js', // sideEffects: ['xx/xx/xx.js', // sideEffects: ['xx/xx/xx.js', // sideEffects: ['xx/xx/xx.js']}Copy the code
Code Splitting/Code Splitting
All of the code will eventually be packaged together, and the bundle size is too large. Most of the time, when the application starts working, not all modules need to be loaded into the bundle, which makes no sense for page loading, etc. The reasonable solution is to divide the package results into multiple bundles, subcontract them, and load them on demand according to the running needs
Http1.1 shortcomings
- You cannot make multiple requests to the same domain name at the same time
- Each request has a certain amount of delay
- Request headers waste loan traffic, so module packaging is necessary
Way of subcontracting
- Multiple entry packing
- ESM dynamic import function, realize module loading on demand
Multiple entry packing
Generally applicable to traditional multi-page applications, the common partition rule is that a page corresponds to a package entry, the common parts of different pages, extracted into the common results
The Settings of multi-entry packaging are as follows:
By default, the html-webpack-plugin outputs an HTML file that automatically injects all the packed results, so to inject the corresponding HTML into the js file of the packed result, we need to specify the bundle that the output HTML needs to inject
Extract the common module Split Chunks
There will certainly be common modules in different entrances. When large three-party JS libraries are used together, the influence will be greater. At this time, public module extraction is needed
Enabling the extract configuration for the public module is simple
This packaging generates bundle.js for the common module part
Dynamic import of Webpack
On-demand loaded to our browser application is very common requirement, need a module, loading a module, greatly save bandwidth and traffic webpack support dynamic on-demand loaded into the way of module, and all the dynamic import module will be automatically extracted to a separate bundle. Js, so as to realize the subcontract
Compared with multi-entry mode, dynamic import is more flexible, and the loading and loading timing of modules are controlled by the logic of the code. The ultimate goal of subcontracting is to make modules load on demand, so as to improve the response speed of applicationsThe figure above shows the dynamic import of the route mapping component to load on demand when routing is configured in the VUE project.
/* webpackChunkName /* webpackChunkName /* webpackChunkName /* webpackChunkName: “BindBankCard” */ will help you, so that the generated bundle.js will be named bindBankcard.bundle.js.
The same webpackChunkName will eventually be packaged together for the business module packaging we need
MiniCSSExtractPlugin extracts CSS into a single file
Is a plug-in that extracts CSS code from the packaging result, enabling on-demand loading of CSS
Using MiniCSSExtractPlugin our style will be kept to a file, also no longer need style – loader, instead of using MiniCSSExtractPlugin. Loader provided in the loader, To achieve the style file through the link tag injection
Use as follows
Personal experience is that CSS code over 150KB needs to be pulled out and introduced separately
When we separate CSS from webpack, we find that the compression plug-in built in Webpack does not compress files other than JS, and the compression of other resource files requires additional plug-in support
Optimize – CSS -assets-webpack-plugin for CSS compression terser-webpack-pluginWebpack built-in JS compression plugin
Output file name hash
Hash File name in production mode Hash is classified into three types, each of which has different effects
filename: '[name]-[hash].bundle.js
At the project level, any place in the project has changed, all the fileshash
Are going to changefilename: '[name]-[chunkhash].bundle.js
.chunk
In the packaging process, as long as the same chunk is packaged,chunkhash
They’re all the same. Change somethingchunk
Only the corresponding contentchunk
thehash
I’m going to change. I’m going to introduce thischunk
The filehash
There will be changesfilename: '[name]-[contenthash].bundle.js
This is file levelhash
Based on the contents of the output filehash
, different fileshash
Different values, modify a file only corresponding to the filechunk
And introduce thischunk
Of the filehash
That will change
Contenthash is probably the best way to solve the cache problem, because it precisely targets the file level hash and only updates the file name when the corresponding file changes
Webpack allows you to specify the length of the hash by specifying the length of the hash as :8 colons, for example filename: ‘[name]-[contenthash:8].bundle.js
For cache control, the 8-bit Contenthash should be the most versatile choice