Original address juejin.cn/post/684490…
preface
2020 is coming, in many front-end recruitment requirements,webpack, engineering these words are becoming more and more frequent. Everyday developers use scaffolding like vue-CLI and create-react-app to build our projects. But if you want to stand out on your team (stand out from the crowd) and get a better offer(pay off your mortgage), then you need to have a good understanding of the Webpack that we deal with a lot
This article is divided into three parts to get you up to speed on WebPack. It should take about 60 minutes to read. If there are any deficiencies, please correct them
This paper is written based on
Webpack 4.41.2
versionNode: 10.15.3
version
1 getting started (let’s familiarize you with webPack configuration with these little examples)
1.1 Initializing the Project
Create a new directory and initialize NPM
NPM init copies the codeCopy the code
Webpack is running in the Node environment and we need to install the following two NPM packages
NPM I -d webpack webpack- CLI Copy codeCopy the code
- NPM I -d stands for NPM install –save-dev
- NPM i-s stands for NPM install –save
Create a new folder SRC, then create a new file main.js, and write some code to test it out
Console. log(' Call me old yuan') copies the codeCopy the code
Configure the package.json command
perform
NPM run build copies the codeCopy the code
If you generate a dist folder with main.js inside it, you have successfully packaged it
1.2 Start our own configuration
Create a new build folder with webpack.config.js in it
// webpack.config.js const path = require('path'); Module. exports = {mode:'development', // Development mode entry: path.resolve(__dirname,'.. / SRC /main.js'), // import file output: {filename: 'output.js', // package filename path: path.resolve(__dirname,'.. /dist') // Packaged directory}} copy the codeCopy the code
Change our packaging command
NPM run build generates the following directory (image). The dist folder in main.js is the file we need to run in the browser
1.3 Configuring an HTML Template
The JS file is packaged, but it is not possible to manually import packaged JS into an HTML file every time
Some of you might think that the js file name is always fixed (output.js). So you don’t have to change the file name every time? In fact, in our daily development, we often have this configuration:
Module. exports = {// omit other configuration output: {filename: '[name].[hash:8].js', // filename path: path.resolve(__dirname,'.. /dist') // Packaged directory}} copy the codeCopy the code
The dist directory file generated at this point is as follows
For caching purposes, you’ll find that the name of the packaged JS file is different every time. Webpack packed JS files that we need to import into THE HTML, but every time we manually change the JS file name is cumbersome, so we need a plug-in to do this for us
NPM i-d html-webpack-plugin copies the codeCopy the code
Create a “public” folder of the same class as “build” and create an “index.html” configuration file
// webpack.config.js const path = require('path'); Const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = {mode:'development', // development mode entry: path.resolve(__dirname,'.. / SRC /main.js'), // import file output: {filename: '[name].[hash:8].js', // package filename path: path.resolve(__dirname,'.. Dist ')}, Plugins :[new HtmlWebpackPlugin({template:path.resolve(__dirname,'../public/index.html')})]Copy the code
The generated directory is as follows (picture)
You can see that the js file generated by the package has been automatically imported into the HTML file
1.3.1 How to develop multi-entry Files
Generate multiple instances of the HTML-Webpack-plugin to solve this problem
const path = require('path'); Const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = {mode:'development', // development mode entry: { main:path.resolve(__dirname,'.. /src/main.js'), header:path.resolve(__dirname,'.. / SRC /header.js')}, output: {filename: '[name].[hash:8].js', // filename path: path.resolve(__dirname,'.. Plugins :[new HtmlWebpackPlugin({template:path.resolve(__dirname,'../ /public/index.html'), Filename :'index.html', chunks:['main'] // module name corresponding to entry file}), new HtmlWebpackPlugin({ template:path.resolve(__dirname,'../public/header.html'), filename:'header.html', Chunks :['header'] // module name corresponding to the entry file}),]} copy codeCopy the code
The following directories are generated
[
](postimg.cc/4m9c76yW)
1.3.2 the clean – webpack – the plugin
Each time we execute NPM run build, we will find that the dist folder remains the last packed file. We recommend a plugin to help us clean the folder clean-webpack-plugin before packing the output
const {CleanWebpackPlugin} = require('clean-webpack-plugin') module.exports = { // ... Omit other configuration plugins:[new CleanWebpackPlugin()]} copy the codeCopy the code
1.4 refer to CSS
Our entry file is JS, so we import our CSS file in the entry JS
We also need some loaders to parse our CSS files
NPM i-d style-loader CSS-loader replicates the codeCopy the code
If we use less to build the style, we need to install two more
NPM I -d less less-loader Copies the codeCopy the code
The configuration file is as follows
// webpack.config.js module.exports = { // ... Omit the other configuration module: {rules: [{$/ test: / \. CSS, use: [' style - loader ', 'CSS - loader'] / / analytical principles from right to left}, {test: / \. Less $/, Use :['style-loader','css-loader','less-loader'] // Parse principles from right to left}]}} copy codeCopy the code
The browser opens the HTML as follows
1.4.1 Adding a Browser prefix to the CSS
NPM I -d postCSs-loader autoprefixer copies codeCopy the code
Configuration is as follows
// webpack.config.js module.exports = { module:{ rules:[ { test:/\.less$/, Use :['style-loader','css-loader',' postCSs-loader ','less-loader'] // Parse principles from right to left}]}} Copy codeCopy the code
Next, we also need to introduce Autoprefixer to make it work, in two ways
1. Create a postcss.config.js file in the root directory of the project and configure it as follows:
Module. exports = {plugins: [require('autoprefixer')]Copy the code
2. Configure it directly in webpack.config.js
// webpack.config.js module.exports = { //... {rules:[{test:/\. Less $/, use:['style-loader','css-loader',{loader:' postCSs-loader ', Options :{plugins:[require('autoprefixer')]}},'less-loader'] // From right to left parsing principle}]}} copy codeCopy the code
At this point we find that CSS is added to the HTML file as a style tag, but if there are many style files, adding them all to the HTML will be confusing. So what we want to do is we want to break out the CSS and introduce it into the CSS file as an external chain and how do we do that? This is where plug-ins help us
1.4.2 split CSS
NPM I-D mini-CSS-extract-plugin copies the codeCopy the code
Before WebPack 4.0, we used the extract-text-webpack-plugin to extract CSS styles from JS files into separate CSS files. After WebPackage 4.0, it is recommended to use the mini-CSs-extract-plugin to package CSS files
The configuration file is as follows
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); module.exports = { //... Module: {rules: [{test: /\. Less $/, use: [ MiniCssExtractPlugin.loader, 'css-loader', 'less-loader' ], } ] }, plugins: [ new MiniCssExtractPlugin({ filename: "[name] [hash] CSS", chunkFilename: "[id]. CSS"})]} copy codeCopy the code
1.4.3 Splitting Multiple CSS
To be more specific, the mini-CSs-extract-plugin we used above consolidates all CSS styles into one CSS file. If you want to split into one-to-one CSS files, we need to use the extract-text-webpack-plugin, which is currently not supported by the Mini-CSS-extract-plugin. We need to install @next version of the extract-text-webpack-plugin
NPM I -d extract-text-webpack-plugin@next // webpack.config.js const path = require('path'); const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin') let indexLess = new ExtractTextWebpackPlugin('index.less'); let indexCss = new ExtractTextWebpackPlugin('index.css'); module.exports = { module:{ rules:[ { test:/\.css$/, use: indexCss.extract({ use: ['css-loader'] }) }, { test:/\.less$/, use: indexLess.extract({ use: [' csS-loader ','less-loader']})}]}, plugins:[indexLess, indexCss]} copy codeCopy the code
1.5 Pack images, fonts, media, and other files
File-loader is used to process files (mainly file names and paths, and file URLS) and move files to output directories. Url-loader is generally used with file-loader and has similar functions to file-loader. If the file size is smaller than the limit. Base64 encoding is returned, otherwise file-loader is used to move the file to the output directory
// webpack.config.js module.exports = {// omit other configurations... Module: {{rules: [/ /... test: / \ | PNG (jpe? G | GIF) $/, / / picture file use: [{loader: 'url - loader, the options: {limit: 10240, fallback: { loader: 'file-loader', options: { name: 'img/[name].[hash:8].[ext]' } } } } ] }, { test: / \. (mp4 | webm | | ogg mp3 | wav | flac | aac) (\? *)? $/, / / media file use: [{loader: 'url - loader, the options: {limit: 10240, fallback: { loader: 'file-loader', options: { name: 'media/[name].[hash:8].[ext]' } } } } ] }, { test: / \. (woff2? | eot | the vera.ttf | otf) (\? *)? $/ I / / font use: [{loader: 'url - loader, the options: {limit: 10240, fallback: {loader: 'file - loader' options: {name: 'fonts / [name] [8] hash: [ext]'}}}}}], copying code]}}Copy the code
1.6 Escape JS files with Babel
In order to make our JS code compatible with more environments we need to install dependencies
NPM I -d babel-loader @babel/preset-env @babel/core replicates the codeCopy the code
- Pay attention to
babel-loader
withbabel-core
Version mapping of
-
Babel-loader 8.x corresponds to babel-core 7.x
-
Babel-loader 7.x Corresponds to babel-core 6.x
// webpack.config.js module.exports = {// omit other configurations… module:{ rules:[ { test:/.js$/, use:{ loader:’babel-loader’, options:{ presets:[‘@babel/preset-env’] } }, Exclude :/node_modules/},]}} Duplicates the code
The above babel-loader will only convert ES6/7/8 syntax to ES5 syntax, but not for new apis such as Promise, Generator, Set, Maps, Proxy, etc. At this point we need to use Babel-Polyfill to help us convert
// webpack.config.js const path = require('path') module.exports = {entry: [" @ Babel/polyfill, "path. Resolve (__dirname, '.. / SRC/index. Js')], / / the entry file} copy codeCopy the code
-
It is more beneficial to read the following articles by typing the above demo once manually. It is recommended for beginners to type more than three times [
The above practice is that we have a preliminary understanding of the functions of Webpack, but in order to skillfully apply in development, we need a system of actual combat. Let’s get rid of the scaffolding and try to build a VUE development environment ourselves
2. Set up the VUE development environment
The small example above has helped us implement packaging CSS, images, JS, HTML, etc. But we also need the following configurations
2.1 Parsing. Vue files
NPM i-d VUe-loader vuE-template-compiler VUE-style-loader NPM i-s VUE copies the codeCopy the code
Vue-loader is used to parse the vue file vue-template-compiler is used to compile the template
const vueLoaderPlugin = require('vue-loader/lib/plugin') module.exports = { module:{ rules:[{ test:/\.vue$/, use:['vue-loader'] },] }, resolve:{ alias:{ 'vue$':'vue/dist/vue.runtime.esm.js', ' @':path.resolve(__dirname,'.. / SRC ')}, extensions: [' * 'and' js', 'json', 'vue']}, plugins: [new vueLoaderPlugin ()]} copy codeCopy the code
2.2 Configuring webpack-dev-server for hot Update
NPM i-d webpack-dev-server copies the codeCopy the code
Configuration is as follows
const Webpack = require('webpack') module.exports = { // ... DevServer :{port:3000, hot:true, contentBase:'.. / dist '}, plugins: [new Webpack. HotModuleReplacementPlugin ()]} copy codeCopy the code
The complete configuration is as follows
// webpack.config.js const path = require('path'); const {CleanWebpackPlugin} = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin') const vueLoaderPlugin = require('vue-loader/lib/plugin') const Webpack = require('webpack') module.exports = { mode:'development', // Entry: {main:path.resolve(__dirname,'.. . / SRC/main js')}, the output: {filename: '[name] [8] hash: js', / / package after the file name path: the path. The resolve (__dirname,'.. / dist ') / / directory} after packaging, the module: {rules: [{test: / \. Vue $/, use: [' vue - loader]}, {test: / \. Js $/, use:{ loader:'babel-loader', options:{ presets:[ ['@babel/preset-env'] ] } } }, { test:/\.css$/, use: ['vue-style-loader','css-loader',{ loader:'postcss-loader', options:{ plugins:[require('autoprefixer')] } }] }, { test:/\.less$/, use: ['vue-style-loader','css-loader',{ loader:'postcss-loader', options:{ plugins:[require('autoprefixer')] } },'less-loader'] } ] }, resolve:{ alias:{ 'vue$':'vue/dist/vue.runtime.esm.js', ' @':path.resolve(__dirname,'.. /src') }, extensions:['*','.js','.json','.vue'] }, devServer:{ port:3000, hot:true, contentBase:'.. /dist' }, plugins:[ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ template:path.resolve(__dirname,'../public/index.html'), filename:'index.html' }), New vueLoaderPlugin (), new Webpack. HotModuleReplacementPlugin ()]} copy codeCopy the code
2.3 Configuring packaging Commands
Now that the package file is configured, let’s test it by creating a new main.js file from SRC
Create a new app.vue
Create a new public folder and create a new index.html folder inside it
NPM run dev if the browser displays the Vue development environment running successfully, congratulations, you have successfully taken the first step
2.4 Distinguish between development environment and production environment
In the actual application of the project, we need to distinguish the development environment and production environment, we added two files on the basis of the original webpack.config.js
-
Webpack.dev.js development environment configuration file
The development environment mainly implements hot updates, no compression code, full sourceMap copy code
-
Webpack.prod. js Production environment configuration file
Production environment is mainly implemented to compress the code, extract CSS files, reasonable sourceMap, segmentation code needs to install the following modules: NPM I -D webpack-merge copy-webpack-plugin optimize- CSS -assets-webpack-plugin uglifyjs-webpack-plugin
-
Webpack-merge Merges configurations
-
Copy-webpack-plugin copies static resources
-
Optimize the CSS – assets – webpack – the plugin CSS
-
Js uglifyjs webpack – plugin compression
Webpack Mode sets production to automatically compress JS code. In principle, there is no need to introduce uglifyjs-webpack-plugin to duplicate the work. But the optimize- CSS -assets-webpack-plugin will destroy the original JS compression at the same time, so here we introduce UglifyJS compression
Against 2.4.1 webpack. Config. Js
const path = require('path') const {CleanWebpackPlugin} = require('clean-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') const vueLoaderPlugin = require('vue-loader/lib/plugin') const MiniCssExtractPlugin = require("mini-css-extract-plugin") const devMode = process.argv.indexOf('--mode=production') === -1; module.exports = { entry:{ main:path.resolve(__dirname,'.. /src/main.js') }, output:{ path:path.resolve(__dirname,'.. /dist'), filename:'js/[name].[hash:8].js', chunkFilename:'js/[name].[hash:8].js' }, module:{ rules:[ { test:/\.js$/, use:{ loader:'babel-loader', options:{ presets:['@babel/preset-env'] } }, exclude:/node_modules/ }, { test:/\.vue$/, use:[{ loader:'vue-loader', options:{ compilerOptions:{ preserveWhitespace:false } } }] }, { test:/\.css$/, use:[{ loader: devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader, options:{ publicPath:"../dist/css/", hmr:devMode } },'css-loader',{ loader:'postcss-loader', options:{ plugins:[require('autoprefixer')] } }] }, { test:/\.less$/, use:[{ loader:devMode ? 'vue-style-loader' : MiniCssExtractPlugin.loader, options:{ publicPath:"../dist/css/", hmr:devMode } },'css-loader','less-loader',{ loader:'postcss-loader', options:{ plugins:[require('autoprefixer')] } }] }, { test:/\.(jep?g|png|gif)$/, use:{ loader:'url-loader', options:{ limit:10240, fallback:{ loader:'file-loader', options:{ name:'img/[name].[hash:8].[ext]' } } } } }, { test:/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, use:{ loader:'url-loader', options:{ limit:10240, fallback:{ loader:'file-loader', options:{ name:'media/[name].[hash:8].[ext]' } } } } }, { test:/\.(woff2?|eot|ttf|otf)(\?.*)?$/i, use:{ loader:'url-loader', options:{ limit:10240, fallback:{ loader:'file-loader', options:{ name:'media/[name].[hash:8].[ext]' } } } } } ] }, resolve:{ alias:{ 'vue$':'vue/dist/vue.runtime.esm.js', ' @':path.resolve(__dirname,'.. /src') }, extensions:['*','.js','.json','.vue'] }, plugins:[ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ template:path.resolve(__dirname,'../public/index.html') }), new vueLoaderPlugin(), new MiniCssExtractPlugin({ filename: devMode ? '[name].css' : '[name].[hash].css', chunkFilename: DevMode? '[id]. CSS' : '[id]. [hash] CSS'}})] duplicate codeCopy the code
Webpack 2.4.2. Dev. Js
const Webpack = require('webpack') const webpackConfig = require('./webpack.config.js') const WebpackMerge = require('webpack-merge') module.exports = WebpackMerge(webpackConfig,{ mode:'development', devtool:'cheap-module-eval-source-map', devServer:{ port:3000, hot:true, contentBase:'.. / dist '}, plugins: [new Webpack. HotModuleReplacementPlugin ()]}) duplicate codeCopy the code
2.4.3 webpack. Prod. Js
const path = require('path') const webpackConfig = require('./webpack.config.js') const WebpackMerge = require('webpack-merge') const CopyWebpackPlugin = require('copy-webpack-plugin') const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') const UglifyJsPlugin = require('uglifyjs-webpack-plugin') module.exports = WebpackMerge(webpackConfig,{ mode:'production', devtool:'cheap-module-source-map', plugins:[ new CopyWebpackPlugin([{ from:path.resolve(__dirname,'../public'), to:path.resolve(__dirname,'../dist') }]), Optimization :{minimizer:[new UglifyJsPlugin({// compress JS cache:true, parallel:true, sourceMap:true}), optimization:{minimizer:[new UglifyJsPlugin({// compress JS cache:true, parallel:true, sourceMap:true}), new OptimizeCssAssetsPlugin({}) ], splitChunks:{ chunks:'all', cacheGroups:{ libs: { name: "chunk-libs", test: / / \ \ / node_modules / \ \ / /, priority: 10, chunks: "initial" / / packaging only initial rely on third-party}}}}}) duplicate codeCopy the code
2.5 Optimize the WebPack configuration
You may be tired of reading this, but if you want to get a better offer and a higher salary, you need to go further
[
](postimg.cc/ZCRcC9tS)
Optimizing the configuration is very practical for us, it is actually related to the size of the file you package, packaging speed and so on. Specific optimization can be divided into the following points:
2.5.1 Optimize packaging speed
Build speed refers to how quickly we update the code after each change and how quickly we package the files before release.
2.5.1.1 Properly Configure mode and Devtool parameters
If the mode parameter is not set, – Webpack4 sets mode default to Production tree shaking and Uglifyjs
2.5.1.2 Narrowing the search scope of files (Include Exclude alias noParse Extensions)
-
Alias: When import ‘vue’ appears in our code, Webpack does a recursive upward search to node_modules. To reduce the search area we can simply tell Webpack which path to look for. This is the configuration of aliases.
-
Include Exclude Also reduces the search conversion time of the WebPack Loader.
-
NoParse When we import jq from ‘jquery’ in our code, WebPack will parse whether the jQ library is dependent on other packages. However, we generally assume that we don’t refer to other packages (except for special ones, you can judge by yourself) for libraries such as jquery. Add a noParse attribute that tells WebPack not to parse to increase packaging speed.
-
Extensions WebPack looks for files based on the suffix defined by extensions (more frequent file types go first)
2.5.1.3 Enabling Multi-process Loader Conversion using HappyPack
Most of the time spent in webPack building is actually spent on loader parsing and conversion and code compression. In daily development, we need to use Loader to convert JS, CSS, pictures, fonts and other files, and the amount of file data to convert is also very large. Due to the single-threaded nature of JS, these conversion operations cannot process files concurrently, but need to process files individually. The basic principle of HappyPack is to reduce the total build time by splitting the task into several sub-processes, which then send the results to the main process
NPM i-d happypack copies the codeCopy the code
2.5.1.4 Use webpack-parallel-ugli-fi -plugin to enhance code compression
The above loader conversion has been optimized, so there is another difficulty is to optimize the compression time of the code.
NPM i-d webpack-parallel-ugli-fi -plugin copies the codeCopy the code
2.5.1.5 Removing a third-party Module
Statically dependent files that do not change often in a development project. Similar to our elementUi, Vue family bucket, etc. Since changes are rare, we don’t want these dependencies to be integrated into every build logic. The advantage of this is that every time I change the files of my native code, WebPack only needs to package the files of my project instead of compiling third-party libraries. In the future, webPack will not pack these libraries as long as we do not upgrade third-party packages, which can quickly improve the packaging speed.
Here, we use the DllPlugin DllReferencePlugin built in WebPack to extract it and create webpack.dll.config.js in the same directory as the webPack configuration file with the following code
// webpack.dll.config.js const path = require("path"); const webpack = require("webpack"); Module. exports = {// Array entry of modules you want to package: {vendor: ['vue','element-ui']}, output: {path: Path.resolve (__dirname, 'static/js'), // File output location filename: '[name].dll. '[name]_library' // this needs to be the same as' name: '[name]_library', 'in webpack.dllPlugin. }, plugins: [ new webpack.DllPlugin({ path: path.resolve(__dirname, '[name]-manifest.json'), name: '[name]_library', context: __dirname }) ] }; Copy the codeCopy the code
Configure the following command in package.json
"DLL" : "webpack - config build/webpack. DLL. Config. Js" duplicate codeCopy the code
Next add the following code to our webpack.config.js
module.exports = { plugins: [ new webpack.DllReferencePlugin({ context: __dirname, manifest: Require ('./vendor-manifest.json')}), new CopyWebpackPlugin([// copy the generated file to dist directory so that no manual CV {from: 'static', to:'static'} ]), ] }; Copy the codeCopy the code
perform
NPM Run DLL copies the codeCopy the code
We’ll find vendor.dll.js that generates the collection third place code we need to import this js file manually in the HTML file
<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, Initial-scale =1.0"> <meta http-equiv=" x-UA-compatible "content=" IE =edge" SRC = "static/js/vendor. DLL. Js" > < / script > < / head > < body > < div id = "app" > < / div > < / body > < / HTML > duplicate codeCopy the code
This way we don’t need NPM run DLLS if we don’t update third-party dependencies. When we directly execute NPM run dev NPM run build, we will find that our packaging speed has been significantly improved. This is because we have removed third-party dependencies through the dllPlugin.
2.5.1.6 Configuring cache
Every time we perform a build, we compile all the files repeatedly. Can this repeated work be cached? The answer is yes, most loaders provide cache configuration items. For example, in babel-loader, caching can be enabled by setting cacheDirectory. CacheDirectory =true writes the result of each compilation to a hard disk file (default: node_modules/. Cache /babel-loader at the root of the project, but you can also customize it)
But what if loader doesn’t support caching? We also have a method, we can use cache-loader, it is very simple to do, babel-loader after the cache is enabled, loader compile results to the hard disk cache. The next build will compare the files and use the cache directly if the files have not changed from the previous one. Use the loader as shown in the official demo. Add the Loader before other loaders that have high performance overhead
NPM i-d cache-loader copies the codeCopy the code
2.5.2 Optimizing the Volume of packed Files
We have optimized the packaging speed, but the file volume after packaging is very large, resulting in slow page loading and waste of traffic, etc. Next, let’s continue to optimize the file volume
2.5.2.1 Importing Webpack-bundle-Analyzer to analyze packaged files
Webpack-bundle-analyzer presents bundles of packaged content as an interactive, intuitive tree that lets us know what we’re really introducing into the packages we’re building
NPM I -D webpack-bundle-Analyzer copies codeCopy the code
Next configure the startup command in package.json
"Analyz ": "NODE_ENV=production npM_config_report =true NPM run build" Copy codeCopy the code
Windows Install NPM I -d cross-env
"Analyz ": "cross-env NODE_ENV=production npM_config_report =true NPM run build" Copy codeCopy the code
The NPM Run Analyz browser will then automatically open a page with the file dependency graph
2.5.2.3 externals
If we want to reference a library, but don’t want webpack, and don’t want to use it globally in programs like CMD, AMD, or Window /global, we can configure Externals. This function is mainly used to create a library, but we can also make full use of Externals in our project development. We remove these static resources that do not need to be packaged from the build logic, and use CDN to reference them.
Sometimes we want a library we’ve introduced through script, such as jquery with CDN, to be used in require, but we don’t want WebPack to compile it into a file. Here the official website case is clear enough, you can click to understand
The webpack official website case is as follows
< script SRC = "https://code.jquery.com/jquery-3.1.0.js" integrity = "sha256 - slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk =" Crossorigin ="anonymous"> </script> module. Exports = {// exports... externals: { jquery: 'jQuery' } }; Import $from 'jquery'; $('.my-element').animate(/* ... * /); Copy the codeCopy the code
2.5.2.3 Tree – shaking
Tree-shaking is mentioned separately because there is a pit. Tree-shaking is mostly used to weed out useless parts of code. Currently, tree-shaking is automatically enabled in webpack4 when we set mode to production. But for this to work, the generated code must be an ES6 module. You cannot use other types of modules such as CommonJS streams. There is a slight problem if Babel is used, because Babel’s preset preset presets any module type to CommonJS by default, which would cause tree-shaking to fail. Fixing this is as simple as setting modules: false in the.babelrc file or in the webpack.config.js file
//. Babelrc {"presets": [["@babel/preset-env", {"modules": false}]]Copy the code
or
// webpack.config.js module: { rules: [ { test: /\.js$/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env', {modules: false}]}}, exclude: /(node_modules)/}]} copy the codeCopy the code
After the baptism of the above two series, we have become a qualified Webpack configuration engineer. But light screw, self substitutability is still very high, we will go into the principle of Webpack
3 Handwritten Webpack series
Having experienced the above two parts, we can skillfully use the relevant Loader and plugin to transform and parse our code. Next, we manually implement loader and plugin to make it more fun in the usual development.
3.1 Handwritten Webpack Loader
Loader is essentially a Node module. It is equivalent to a juicer giving it file codes of relevant types. According to the rules we set, the processed juice (code) will be returned to us after a series of processing.
Loader writing Principles
- Single principle: every
Loader
Do only one thing; - Chain call:
Webpack
Each will be called in a sequential chainLoader
; - Unity principle: Follow
Webpack
Formulate design rules and structure, input and output are strings, eachLoader
Fully independent, plug and play;
In everyday development environments, we tend to include a lot of console printing for debugging purposes. But we don’t want printed values in production. So here we implement a loader ourselves to remove the console from the code
AST for knowledge popularization. AST in layman’s terms, suppose we have a file a.js and we do something to the 1000 lines in a.js, such as adding try catches to all await and so on, but the code in A.js is essentially a bunch of strings. So what we can do is convert it to an object with marked information (an abstract syntax tree) that we can add, delete, change and look up easily. This tagged object (abstract syntax tree) is the AST. Here is a good AST article AST quick start
NPM I -d @babel/parser @babel/traverse @babel/generator @babel/types Copy codeCopy the code
@babel/parser
Parse the source code intoAST
@babel/traverse
对AST
The node is recursively traversed to generate an easy to operate, transformpath
object@babel/generator
将AST
Decoding generatedjs
code@babel/types
Through the module for specificAST
Add, delete, change, and query nodes
The new drop – the console. Js
const parser = require('@babel/parser') const traverse = require('@babel/traverse').default const generator = require('@babel/generator').default const t = require('@babel/types') module.exports=function(source){ const ast = parser.parse(source,{ sourceType: 'module'}) traverse(ast,{ CallExpression(path){ if(t.isMemberExpression(path.node.callee) && t.isIdentifier(path.node.callee.object, {name: "console"})){ path.remove() } } }) const output = generator(ast, {}, source); Return output.code} Copy codeCopy the code
How to use
const path = require('path') module.exports = { mode:'development', entry:path.resolve(__dirname,'index.js'), output:{ filename:'[name].[contenthash].js', path:path.resolve(__dirname,'dist') }, module:{ rules:[{ test:/\.js$/, Use :path.resolve(__dirname,'drop-console.js')}]}} Copy codeCopy the code
In fact, removing the console is already integrated in webpack4 and can be configured in minimizer to remove the console
How to write a loader
3.2 Handwritten Webpack Plugin
A number of events are broadcast during the life cycle of a Webpack run, and the Plugin can listen for these events and change the output when appropriate through the API provided by Webpack. In layman’s terms: A dish of delicious scrambled eggs with salted beans needs to go through the process of frying with oil and seasoning to the final loading, etc. Plugin can monitor every link and operate, for example, a plugin can be written to monitor the life cycle events exposed by Webpack (seasoning), and perform the operation of reducing pepper when seasoning. So what distinguishes it from Loader? Loader can only do one thing. For example, less-loader can only parse less files. Plugin can perform a wide range of tasks for the whole process.
A basic plugin plugin structure is as follows
class firstPlugin { constructor (options) { console.log('firstPlugin options', options) } apply (compiler) { compiler.plugin('done', Compilation => {console.log('firstPlugin'))}} module.exports = firstPluginCopy the code
Compiler, compilation what is?
compiler
The object containsWebpack
All configuration information for the environment. This object is starting upwebpack
Is created once and configured with all actionable Settings, includingoptions
.loader
和plugin
. When inwebpack
When a plug-in is applied to the environment, the plug-in receives thiscompiler
Object reference. You can use it to accesswebpack
The main environment of.compilation
Object contains the current module resources, build resources, changing files, and so on. When runningwebpack
When developing environment middleware, each time a file change is detected, a new one is createdcompilation
To generate a new set of compile resources.compilation
Object also provides a number of critical timing callbacks that plug-ins can choose to use when doing custom processing.
The difference between compiler and compilation is
-
Compiler represents the entire WebPack lifecycle from startup to shutdown, while compilation simply represents a new compilation process
-
Compiler and compilation expose a number of hooks that can be customized according to the actual scenarios we need
Compiler hook document
Compilation hook document
Let’s manually develop a simple requirement to automatically generate information about the size of the packaged file before generating it
Create a new webpack-firstplugin.js
class firstPlugin{ constructor(options){ this.options = options } apply(compiler){ compiler.plugin('emit',(compilation,callback)=>{ let str = '' for (let filename in compilation.assets){ str += ${compilation. Assets [filename]['size']()}\n '} [' filesize.md '] = {source:function(){return STR}, Size :function(){return str.length}} callback()})}} module.exports = firstPluginCopy the code
How to use
Const path = require('path') const firstPlugin = require(' webpack-firstplugin.js ') module.exports = {// omit other code Plugins :[new firstPlugin()]} Copy codeCopy the code
Execute NPM run build to see filesize.md generated in the dist folder with information about the packaged files
The above two loader and Plugin cases are only a guide. There are many aspects of loader and plugin to consider in the actual development requirements. It is recommended that you try more by yourself.
How to write a plugin on the official website
3.3 write a webpack
Because the length is too long, and the principle is more in-depth. In view of this quick start to practical development principle, we decided to write a new article that dissects webPack principles and implements a demo version. Once the format is calibrated, a link will be posted below
4 WebPackage 5.0 era
Both front-end frameworks and build tools are being updated faster than we ever imagined, and the jquery of a few years ago is gone forever. We want to embrace the constant iteration of Vue, React, Node, Serverless, Docker, K8S….
Not to be outdone, WebPack has recently released WebPack 5.0.0 Beta 10. Webpack5.0 is designed to reduce configuration complexity and make it easier to get started (as was said in webpack4), as well as some performance improvements
- Use persistent caching to improve build performance;
- Improve long-term caching with better algorithms and default values;
- Clean up the internal structure without introducing any disruptive changes;
- Introduce some breaking changes to use v5 for as long as possible.
Currently, updates from maintainers are frequent, and it won’t be long before webpack5.0 will embrace the masses. If you’re interested, you can install the beta version first. But before that, I suggest you to master Webpack4, so that the road behind will be more and more easy to go.
Author: Huang Xiaobug Link: juejin.cn/post/684490… The copyright belongs to the author. Commercial reprint please contact the author for authorization, non-commercial reprint please indicate the source.