It’s been a long time coming and I’ve been wanting to get a better look at Webpack. I’ve been so tired of Webpack that I’ve preferred a zero-configuration module packaging tool like Parcel, but it’s actually more solid and more powerful. Since I didn’t know about Node.js when I learned Webpack last time, I really felt powerless and even the __dirname was very complicated. After learning Node.js, I will understand Webpack better. This time I will have a deeper understanding of Webpack. Try to get rid of scaffolding tools like create-React-app or VUe-CLI, or write your own scripts to automatically configure the development environment.
Since Webpack 4.0 has been released at the time of writing this note, this note can be considered as a note for learning Webpack 4.0. The version I use is Webpack 4.8.3. In addition, using Webpack 4.x command line requires a separate command line tool. The Webpack command-line tool used by the author is Webpack-CLI 2.1.3, and you can deploy the development environment according to this requirement while learning.
In addition, you should have some knowledge of ES6, Node.js, and use a scaffold before learning WebPack.
1. Core Concepts
Webpack has four core concepts that you need to understand to get started with Webpack. These are Entry, Output, Loader, and Plugins. These four core concepts are described in detail.
1.Entry
Entry is the Entry starting point for Webpack, which indicates which module Webpack should start with as it builds its internal dependency graph. You can configure the Entry property in the configuration file (webpack.config.js) to specify one or more entry points, which defaults to./ SRC (webpack 4 is starting to introduce defaults).
Specific configuration methods:
entry: string | Array<string>
Copy the code
The former a single string is configured as a single entry file, while the latter (an array) is configured as a multi-file entry.
It can also be configured via object syntax:
entry: {
[entryChunkName]: string | Array<string>
}
Copy the code
Such as:
//webpack.config.js
module.exports = {
entry: {
app: './app.js',
vendors: './vendors.js'
}
};
Copy the code
The above configuration means that the dependency trees are packaged and built from the APP and vendors attributes. The advantage of this is to separate the business logic code you develop from the source code of third-party libraries, which basically stay the same after the third-party libraries are installed. This separation of packaging speeds up packaging and reduces the number of packaged files. Vue-cli takes this separate packaging pattern. However, this syntax may be discarded in favor of the DllPlugin plugin, which is better for splitting code.
2.Output
The Output attribute tells WebPack where to export the bundles it creates and can also specify the name of the bundles./dist is the default location. The entire application structure is compiled into the specified output folder. The most basic properties include filename and path.
It is worth noting that even if you configure multiple entry files, you can only have one output point.
Specific configuration methods:
output: {
filename: 'bundle.js',
path: '/home/proj/public/dist'
}
Copy the code
It is worth noting that output.filename must be an absolute path; if it is a relative path, Webpack will throw an exception when packing.
For multiple entries, print multiple bundles using the following syntax:
// webpack.config.js
module.exports = {
entry: {
app: './src/app.js',
vendors: './src/vendors.js'
},
output: {
filename: '[name].js',
path: __dirname + '/dist'
}
}
Copy the code
The above configuration will output the packaged files app.js and vendors. Js to __dirname + ‘/dist’.
3.Loaders
Loader can be understood as the compiler of Webpack, which enables WebPack to process some non-javascript files, such as PNG, CSV, XML, CSS, JSON and other types of files. With the appropriate loader, JavaScript imports can be imported into non-javascript modules. JavaScript only considers JavaScript files as modules, while Webpack is designed with the idea that everything is a module. In order for Webpack to be able to recognize other “modules”, loader is needed as a “compiler”.
Configuring a Loader in Webpack has two goals:
- (1) The test attribute: marks which suffixes the file should be processed, is a regular expression.
- (2) Use attribute: specify which loader should be used for preprocessing files of type test.
Such as webpack. Config. Js:
module.exports = { entry: '... ', output: '... ', module: { rules: [ { test: /\.css$/, use: 'css-loader' } ] } };Copy the code
This configuration file indicates that all CSS files should be imported through CSS-loader. After csS-loader is imported, you can directly import CSS modules using import statements in JavaScript modules. However, before using CSS-Loader, use NPM to install CSS-Loader.
Note here that loaders rules are defined not in the rules property of the object, but in the Rules property of the Module property.
Configure multiple Loaders:
Sometimes, importing a module may need to use multiple Loaders for preprocessing, at this time, it is necessary to configure multiple Loaders for preprocessing of the specified type of file, configure multiple Loaders, assign the use attribute to an array, webpack will follow the sequence of the loaders in the array, The corresponding Loader is used to preprocess module files.
{
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader'
}
]
}
]
}
}
Copy the code
In addition, you can use inline mode for Loader configuration:
import Styles from 'style-loader! css-loader? modules! ./style.css'Copy the code
But this is not the recommended approach, so use module.rules as much as possible.
4.Plugins
Loaders are used to convert non-javascript files, while plug-ins can be used to perform a wide range of tasks, including packaging, optimization, compression, server building, and more. If you use a plug-in, you typically install it using the NPM package manager, introduce it in the configuration file, and instantiate it and pass it to the plugins array properties.
Plugins are the backbone of WebPack, and are currently used to solve many other complex functions that loader cannot implement. Plugins are used via plugins:
// webpack.config.js
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.optimize.UglifyJsPlugin()
]
}
Copy the code
Just pass an array of instances to the plugins property.
5.Mode
Mode can be configured by configuring the Mode property of the object, the main value being production or development. The difference between the two patterns is that one compiles the packaging for production and the other compiles the packaging for development. In production mode, WebPack automatically compresses and optimizes the code, saving configuration trouble.
After learning the basic concepts above, you can basically start webpack, because the power of Webpack is built on these basic concepts, using webpack’s various loaders and plugins, you can achieve a powerful packaging function.
Two, basic configuration
Follow these steps to implement webPack’s simple packaging capabilities:
-
(1) Create the project folder, location and name, and change the current path of CMD or git bash to the project folder.
-
(2) Install webpack and webpack-CLI into the development environment:
npm install webpack webpack-cli --save-dev Copy the code
-
(3) Create the following files and directories under the project folder:
- /src
- index.js
- index.css
- /dist
- index.html
- webpack.config.js
- /src
-
(4) Installing CSS-Loader:
npm install css-loader --save-dev Copy the code
-
(5) Configure webpack.config.js:
module.exports = { mode: 'development', entry: './src/index.js', output: { path: __dirname + '/dist', filename: 'bundle.js' }, module: { rules: [ { test: /\.css$/, use: 'css-loader' } ] } }; Copy the code
-
Add bundle.js to index.html:
<! --index.html--> <html> <head> <title>Test</title> <meta charset='utf-8'/> </head> <body> <h1>Hello World! </h1> </body> <script src='./bundle.js'></script> </html>Copy the code
-
(7) Add to index.js:
import './index.css'; console.log('Success! ');Copy the code
-
(8) In the project directory, use the following command to package:
webpack Copy the code
To view the output, double-click /dist/index.html to see any errors and the console output.
How to use Webpack with Node script?
Webpack provides a Node API that makes it easy to use Webpack in Node scripts.
The basic code is as follows:
// Introduce the Webpack module. const webpack = require('webpack'); // Import configuration information. const config = require('./webpack.config'); // Pass the config configuration information directly through the webpack function. const compiler = webpack(config); // The plug-in is applied through the Apply method of the Compiler object and can also be configured in the configuration information. compiler.apply(new webpack.ProgressPlugin()); // Start packing by running webpack using the Compiler object's run method. Compiler.run ((err, stats) => {if(err) {// An error message is received in the callback. console.error(err); } else {// Receive specific feedback on the success of packaging in the callback. console.log(stats); }});Copy the code
Dynamically generate index.html and bundle.js
What is dynamic generation? Dynamic generation refers to inserting hash values into the packaged module name, so that each generated module has a different name. The reason why index.html is generated dynamically is that the generated module name is different each time the package is generated, so the script tag must be changed when referencing in the HTML file. This ensures that the correct JavaScript file is referenced every time.
Why add a hash value?
The reason for dynamically generating the bundle files is to prevent the browser cache mechanism from blocking file updates. After each code change, the hash in the file name changes, forcing the browser to refresh and retrieve the latest file.
How do I add a hash to a bundle?
When setting output, add [hash] to the filename in output.filename, for example:
// webpack.config.js
module.exports = {
output: {
path: __dirname + '/dist',
filename: '[name].[hash].js'
}
};
Copy the code
Now that you can dynamically generate bundles, how do you dynamically add bundles to HTML files?
The name of the bundle changed every time it was packaged, and every time you artificially modified the corresponding HTML file to add references to JavaScript files, you needed to use the powerful Webpack plugin. There’s a plugin called htMl-webpack-plugin. HTML files can be generated automatically. Install to the development environment:
npm install html-webpack-plugin --save-dev
Copy the code
After installation, import it in webpack.config.js and add examples to plugins:
// webpack.config.js const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { // other configs ... Plugins: [new HtmlWebpackPlugin({// options})]}; plugins: [new HtmlWebpackPlugin({// options})]};Copy the code
You can see that each time the bundle is generated, the corresponding HTML file is dynamically generated.
You can also see in the code above that the constructor of the HtmlWebpackPlugin plugin can also pass a configuration object as an argument. Some useful configuration attributes are title (specifying the content of the TITLE tag in the HTML and the title of the page), template (specifying the template HTML file), etc. For more information, visit html-webpack-plugin
Clean up the /dist folder
Since the JavaScript files are generated with different names each time, the new files will not overwrite the old files, and the old files will be stored in the /dist folder. As the compilations increase, the folder will get bigger and bigger. Therefore, you should try to clean the /dist folder before generating a new bundle. To ensure that the folder is clean and tidy, there are two better ways to deal with:
If you are Node script calling webpack pack:
If you use the Node API to call Webpack for packing, you can use Node’s fs module directly to delete all files in the /dist folder before packing:
const webpack = require('webpack'); const config = require('./webpack.config'); const fs = require('fs'); const compiler = webpack(config); var deleteFolderRecursive = function(path) { if (fs.existsSync(path)) { fs.readdirSync(path).forEach(function(file, index){ var curPath = path + "/" + file; if (fs.lstatSync(curPath).isDirectory()) { // recurse deleteFolderRecursive(curPath); } else { // delete file fs.unlinkSync(curPath); }}); fs.rmdirSync(path); }}; deleteFolderRecursive(__dirname + '/dist'); compiler.run((err, stats) => { if(err) { console.error(err); } else { console.log(stats.hash); }});Copy the code
You can see that the custom deleteFolderRecursive method was used to delete all files in the /dist directory before calling compiler.run for packing.
If you use Webpack-CLI for packaging
This is done through the webpack plugin, the clean-webpack-plugin.
Installation:
npm install clean-webpack-plugin --save-dev
Copy the code
Then add the plugin to the webpack.config.js file:
// webpack.config.js
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
plugins: [
new CleanWebpackPlugin(['dist'])
]
};
Copy the code
When you pack it again, you will find that all of the previous package files have been deleted.
Vi. Build the development environment
There are many differences between the development environment and the production environment. The production environment pays more attention to the production efficiency, so the code must be compressed and streamlined, and some debugging tools that are not needed in the production environment must be removed, and the application efficiency and performance can be improved. The development environment pays more attention to debugging and testing. In order to facilitate development, we need to build a suitable development environment.
(I) Use Source Maps for debugging
Why use Source Maps?
Because webPack packs the source code to compress, simplify, and even replace variable names, you can’t debug line by line in the browser, so you need to use Source Maps, which allows you to see the source code in the browser and debug line by line.
How to use Source Maps?
Add the devtool attribute to the configuration and assign it to source-map or inline-source-map, which gives a more specific error message indicating a specific error location in the source code, whereas the source-map option does not indicate a specific error location in the source code.
(2) Use development tools
Having to manually enter a command or start a Node script to compile every time you write the code and save it can be annoying. Choose a tool to simplify the development process:
- Enable Watch mode
- Use the webpack – dev server. –
- Use the webpack – dev – middleware
(1) Use watch mode
When using webpack-CLI, you can start watch mode by commanding webpack –watch. Once in Watch mode, webpack will be recompiled if a module in the dependency tree changes.
(2) use webpack-dev-server
Anyone who has used scaffolding like create-react-app or vue-CLI knows that you can set up a local server with the command NPM run start, and webPack automatically opens the browser to the page you’re working on, and the browser refreshes as soon as you modify the file. Basically what you see is what you get, more convenient and powerful than Webpack’s Watch mode.
Usage:
-
Install webpack-dev-server
npm install --save-dev webpack-dev-server Copy the code
-
② Modify the configuration file and add the devServer property:
// webpack.config.js module.exports = { devServer: { contentBase: './dist' } }; Copy the code
-
③ Add command attributes to package.json:
// package.json { "scripts": { "start": "webpack-dev-server --open" } } Copy the code
-
④ Run commands
npm run start Copy the code
You can see what the browser actually looks like when it opens, try modifying the file, and see if the browser is updating in real time.
In addition, you can specify more configuration information under the devServer property, such as the port of the development server, hot update mode, whether to compress, and so on, specifically: Webpack
Using webpack-dev-server via the Node API:
'use strict'; const Webpack = require('webpack'); const WebpackDevServer = require('.. /.. /.. /lib/Server'); const webpackConfig = require('./webpack.config'); const compiler = Webpack(webpackConfig); const devServerOptions = Object.assign({}, webpackConfig.devServer, { stats: { colors: true } }); const server = new WebpackDevServer(compiler, devServerOptions); Listen (8080, '127.0.0.1', () => {console.log('Starting server on http://localhost:8080'); });Copy the code
Use Webpack-dev-middleware
Webpack-dev-middleware is a more basic plugin than Webpack-dev-server, which also uses webpack-dev-server. It’s more complex to use, but low encapsulation means high customization, and webpack-dev-Middleware allows you to define more Settings to meet more development needs, based on the Express module.
I won’t cover this too much, because Webpack-dev-server can handle most development scenarios without having to set any more Express properties. For those who want to learn more, use Webpack-dev-Middleware
(4) Set up the IDE
Some ides have write security, which prevents the IDE from saving files when the development server is running.
Specific reference: adjust the text editor
(3) Replace the hot module
Hot Module Replacement (HMR) means that modules are replaced, added, or deleted during the running of an application, and the corresponding changes can be displayed by the browser without refreshing the page.
Usage:
-
(1) add hot to devServer attribute and set it to true:
// webpack.config.js module.exports = { devServer: { hot: true } } Copy the code
-
(2) Introduce two plug-ins into the WebPack configuration file:
// webpack.config.js const webpack = require('webpack'); module.exports = { devServer: { hot: true }, plugins: [ new webpack.NamedModulesPlugin(), new webpack.HotModuleReplacementPlugin() ] }; Copy the code
-
(3) Add code at the bottom of the entry file so that webPack can be notified when all code changes:
if (module.hot) { module.hot.accept('./print.js', function() { console.log('Accepting the updated intMe module!'); printMe(); }) } Copy the code
Hot module replacement is difficult to control and prone to errors. You are advised to use different Loaders for different development configurations to simplify the HMR process. Specific reference: other code and frameworks
7. Build the production environment
The production environment requires streamlined code and excellent performance, while the development environment requires rapid development and easy testing, so the purpose of WebPack packaging in the two environments is not the same, so it is best to separate the configuration files in the two environments. For separate configuration files, webpack-merge is a good consolidation tool (vue-CLI is also used).
Usage:
-
(1) Install webpack-merge
npm install webpack-merge --save-dev Copy the code
-
(2) Establish three configuration files:
- webpack.base.conf.js
- webpack.dev.conf.js
- webpack.prod.conf.js
Webpack.base.conf. js indicates basic configuration information, such as entry, output, and Module, that needs to be set in both development and production environments. Configure some environment-specific information in the other two files, and then integrate it with webpack.base.conf.js using the webpack-merge module.
-
(3) Add NPM scripts
// package.json { "scripts": { "start": "webpack-dev-server --open --config webpack.dev.conf.js", "build": "webpack --config webpack.prod.conf.js" } } Copy the code
In addition, it is recommended to set the mode attribute, because code compression is automatically turned on in production environments, eliminating the need for configuration.
Eighth, performance optimization
TreeShaking
TreeShaking stands for removing unused code from JavaScript files, and WebPack 4 enhances this part of the functionality. By configuring the sideEffects property of package.json, you can specify which files can remove excess code. If sideEffects is set to false, unused code in the file can be safely removed without sideEffects. If redundant code in some files cannot be removed, you can set the sideEffects property to an array containing the path string of the file.
After specifying a file with no side effects, set mode to “production” and build the code again, you can see that unused code has been removed.
Tips
- in
module.rules
Property to specify which files need to be processed by the Loader. - Use only the necessary loaders.
- Keep it up to date.
- Reduce project files.
Build PWA applications with Webpack
Progressive Web Application-PWA is a Web app that provides a similar experience to a native app, enabling the Application to continue running when offline. This is achieved through the Service Workers technology. PWA is a popular concept in recent years. Its core is a proxy server built between the client browser and the server by the Service worker technology. When the network is smooth, the client browser will access the server through the service worker and cache the registered files. When the network is disconnected, the browser will visit the proxy server of Service worker, so that the page can still be accessed even when the network is disconnected, and the website development similar to the native application is realized. Create-react-app already implements PWA configuration.
Here’s how to develop PWA quickly with WebPack.
-
(1) Install workbox-webpack-plugin:
npm install workbox-webpack-plugin --save-dev Copy the code
-
(2) Add the plugin to the configuration file:
// webpack.config.js const WorkboxPlugin = require('workbox-webpack-plugin'); module.exports = { plugins: [ new WorkboxPlugin.GenerateSW({ clientsClaim: true, skipWaiting: true }) ] }; Copy the code
-
(3) Compile with Webpack and package service-worker.js
-
(4) Register service worker at the bottom of entry file:
if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/service-worker.js').then(registration => { console.log('SW registered: ', registration); }).catch(registrationError => { console.log('SW registration failed: ', registrationError); }); }); } Copy the code
-
(5) Open the page for debugging:
npm run start Copy the code
-
(6) Open the browser debugging tool and check the console output. If “SW registered:… …” , indicating that the service worker has been registered successfully. Then you can disconnect the network or shut down the server and refresh again. You can see that the page is still displayed.
10. Refer to the article
- Webpack official Chinese document
Xi. Conclusion
Webpack is indeed a powerful module packaging tool, rich loaders and plugins make it more powerful. Learning WebPack allows you to customize your own development environment without relying on scaffolding like creation-React-app and VUe-CLI, and to tailor your code to your needs. If you want to build a complex development environment and production environment, you still need to know a lot of loaders and plugins. Fortunately, webPack official website provides all the instructions for users to use:
- webpack loaders
- webpack plugins
Reading scaffolding source code is also helpful to learn webpack, there should be more study in this area in the future, but the defense is coming, I don’t know if there is a chance before graduation.