1, Basic elements
1,Entry/Output
1.1. Single entry configuration
module.exports = { entry: './src/index.js', // Packaged entry file output: './dist/main.js', // Packaged output };
1.2. Multi entry configuration
const path = require('path'); module.exports = { entry: { app: './src/app.js', admin: './src/admin.js', }, output: {, filename: '[name].[hash].js', //You can choose to set hash by ensuring that the file name is unique through placeholders path: path.join(__dirname, 'dist'), // publicPath is used to set the baseUrl for loading static resources. For example, it points to cdn in prod mode and local service in dev mode publicPath: process.env.NODE_ENV === 'production' ? `//cdn.xxx.com` : '/', // }, };
2,Loaders
The Loaders function takes the file type as a parameter and returns the result of the conversion. At present, the two types supported by webpack are JS and JSON, and other types need to be converted
2.1. Universal Loaders
module:{ rules:[ {test:/.\(js|jsx|ts|tsx)$/,use:'ts-loader'} // For example, use ts loader ] },
2.2. Inline Loaders
Loaders can also be used directly inline into code:
import 'style-loader!css-loader!less-loader!./style.less';
2.3. Multiple Loaders
The execution order among multiple Loaders is the opposite of the rules configuration, that is, from right to left
2.3.1,Source logic
loader comes in first and comes out last, and the corresponding stack out sequence is from right to left
if (matchResourceData === undefined) { for (const loader of loaders) allLoaders.push(loader); for (const loader of normalLoaders) allLoaders.push(loader); } else { for (const loader of normalLoaders) allLoaders.push(loader); for (const loader of loaders) allLoaders.push(loader); // Push } for (const loader of preLoaders) allLoaders.push(loader); // pre loaders stack
2.3.2 change order
Change the execution order by configuring enforce. Enforce has four enumeration values, and its execution order is pre, normal, inline and post
module:{ rules:[ { test:/\.less$/, loader:'less-loader', enforce:'pre' // Pretreatment }, { test: /\.less$/, loader:'css-loader', enforce:'normal' // The default is normal }, { test: /\.less$/, loader:'style-loader', enforce:'post' // Post processing }, ] },
3,Plugins
Plugins is responsible for optimizing bundle files, resource management and environment variable injection. webpack has many built-in plugins. For example, DefinePlugin global variable injection plug-in, IgnorePlugin exclude file plug-in, ProgressPlugin package progress bar plug-in, etc
plugins: [new HtmlwebpackPlugin({ template: './src/index.html' })];
4,Mode
There are three options for specifying the current build environment: production, development and none. When the mode is production, the built-in optimization plug-ins will be enabled, such as TreeShaking, ScopeHoisting, compression plug-in, etc
module.exports = { mode: 'production', // Is written to the environment variable NODE_ENV };
You can also set it through the webpack cli parameter
webpack --mode=production
2, Hot renewal
1. Update process
1.1. Startup phase 1 - > 2 - > A - > b
- Compile JS files into bundles through webpack compile
- Run the Bundle file on the Bundle Server so that the file can pass through the localhost://xxx visit
- Then build the output bundle JS file to the browser
1.2. Hot update phase 1 - > 2 - > 3 - > 4
- Webpack compile compiles JS files into bundles
- Run the Bundle file on the HMR Server
- Once the file in the disk is modified, the modified information will be output to the HMR Runtime
- Then, the HMR Runtime partially updates the file changes
2. Configuration mode
2.1,WDS + HotMoudleReplacementPlugin
2.1.1,WDS(webpack-dev-server)
WDS provides the ability of bundle server. Instead of outputting files, it is placed in memory, that is, the generated bundle JS files can be localhost://xxx At the same time, it provides the liveload capability, so that the browser can refresh automatically
// package.json "scripts":{ "dev":"webpack-dev-server --open" }
2.1.2. HotMoudleReplacementPlugin
The HotMoudleReplacementPlugin plug-in provides WDS with the ability of hot update, which comes from its HMR Runtime with the ability to locally update pages. Once the file in the disk is modified, the HMR Server sends the modified js module information to the HMR Runtime
// webpack.dev.js is only used in the development environment module.exports = { mode: 'development', plugins: [new webpack.HotModuleReplacementPlugin()], devServer: { contentBase: './dist', //Service base directory hot: true, //Turn on hot update }, };
2.1.3 interaction logic
When listening to the file modification, HotMoudleReplacementPlugin will generate a mainifest and update file, in which mainifest describes the changed modules, followed by webpack dev server notifying the client of the update code through websocket, and the client uses jsonp to request the server to obtain the updated code
2.2,WDM(webpack-dev-middleware)
WDM transfers the file output from the webpack to the server, which is suitable for flexible customization scenarios
const express = require('express'); const webpack = require('webpack'); const webpackDevMiddleware = require('webpack-dev-middleware'); const app = express(); const config = require('./webpack.config.js'); const compiler = webpack(config); app.use( webpackDevMiddleware(compiler, { publicPath: config.output.publicPath, }), ); app.listen(3000, function () { console.log('listening on port 3000'); });
3, File fingerprint
The file fingerprint is mainly used for version management, which is expressed in the suffix of the file name after packaging, such as XXX / / xxx_ 51773db. 51773db in JS
1. Three types
type | meaning |
---|---|
Hash | It is related to the construction of the whole project. As long as the project file is modified, the hash value of the whole project construction will change |
Chunkhash | Related to the chunk packaged by webpack, different entries will generate different chunkcash values |
Contenthash | The hash is defined according to the file content. If the file content remains unchanged, the contenthash remains unchanged |
2. Common scenes
- Set the filename of output and use [chunkash]
filename: '[name][chunkhash:8].js';
- Set the filename of MiniCssExtractPlugin and use [contenthash]
new MiniCssExtractPlugin({ filename: `[name][contenthash:8].css`, });
- Set the name of the file loader and use [hash]
rules: [ { test: /\.(png|svg|jpg|gif)$/, use: [ { loader: 'file-loader', options: { name: 'img/[name][hash:8].[ext]', }, }, ], }, ]; // Placeholder explanation: [name]: file name, [ext]: resource suffix
Note: hash is generated by code and path. Therefore, when the same code is packaged and deployed on multiple machines, the hash will be different, resulting in resource loading 404. Generally, it is packaged by one machine, distributed and deployed to different machines
4, SourceMap
1. Open configuration
The development environment is turned on and the online environment is turned off. When troubleshooting online, you can upload the source map to the error monitoring system
module.exports = { devtool: 'source-map', };
2. Type
type | explain |
---|---|
cheap-source-map | No column number, only line number, fast |
cheap-module-source-map | The optimized soap source map avoids the mismatch of line numbers of compiled code such as babel |
eval | Determine the code path through the inline code eval function baseURL |
eval-source-map | The sourcemap is placed after the eval function |
inline-source-map | At the end of the packaged code |
3. File format
The source string can be restored by using the mappings mapping table, names and sourcesContent
{ "version": 3, // Source Map version "file": "out.js", // Output file (optional) "sourceRoot": "", // Source file root directory (optional) "sources": ["foo.js", "bar.js"], // Source file list "sourcesContent": [null, null], // Source content list (optional, in the same order as the source file list) "names": ["src", "maps", "are", "fun"], // List of symbol names used by mappings "mappings": "A,AAAB;;ABCDE;" // String with encoded mapping data }
5, TreeShaking
- The code will not be executed and cannot be reached
- The results of code execution are not used
- The code will only affect dead variables (write only but not read)
TreeShaking will eliminate the above code as obsolete in the uglify stage
When the mode is set to production, it is enabled by default. Through in Set modules:false in babelrc to cancel
TreeShaking uses the characteristics of ES6 module to clear
- import can only appear as a statement at the top level of the module, and the module name can only be a string constant
The import module is statically loaded and obtains variable references, that is, when the module changes internally, the imported variables will also change. Therefore, import cannot appear in statements such as conditions and functions (similar to export), while require in commonjs obtains the cache of the module
- import binding is immutable
6, Module mechanism
After the webpack is packaged, a layer of package will be added to the module, and the import will be converted into__ webpack_require
1. Anonymous closure
After the webpack is packaged, it is an anonymous closure. The received parameter modules is an array, and each item is a module initialization function. Pass__ webpack_require loads the module and returns modules exports,
Each module member of modules uses__ webpack_require__ installedModules is the cache of the loaded module. If it is already loaded__ webpack_require__ If it is loaded, it does not need to be loaded again.
2,ScopeHoisting
There are a large number of closure codes in the built code, resulting in the increase of function scopes created at runtime and large memory overhead. ScopeHoisting puts the codes of all modules in one function scope according to the reference order, and then renames some variables appropriately to prevent variable name conflicts, so as to reduce the function declaration code and memory overhead
7, SSR
The core of SEO friendly server rendering SSR is to reduce requests, so as to reduce white screen time. The implementation principle is: the server renders the React component into a string through the renderToString method of React DOM / server, and returns the template corresponding to the route. The assisted client generates components for the server through packaging
renderToString carries the data reactive attribute, which can be used with hydrate. It will reuse the previous nodes and only bind events, so as to optimize the first rendering speed. A similar method is rendertostatic markup
1. Compatibility issues
1.1. Browser global variables
- node.js has no document and window, so it needs to be adapted through the packaging environment
In the react ssr application, reading document and window can be done in useEffect or componentDidMount. When nodejs is rendered, these executions will be skipped to avoid error reporting
- Replace fetch and xhr with isomorphic fetch or axios
1.2 style problems
- node.js cannot parse css. Ignore loader can be used to ignore css parsing
For the antd component library, set style to false in Babel plugin import
- Replace style loader with isomorphic style loader
2. Cooperation at both ends
Use the packaged HTML as the template, and replace the placeholder after the server obtains the data
<body> <div id="root"> <!--HTML_PLACEHOLDER--> </div> <!--INITIAL_DATA_PLACEHOLDER--> </body>
8, Common optimization measures
1. Code compression
1.1 compression of JS files
-
Built in uglifyjs webpack plugin
-
Commons chunkplugin extracts the common modules in chunks to reduce the total volume
1.2 compression of CSS files
-
Use optimize CSS assets webpack plugin and cssnano
-
Extract text webpack plugin separates css from the product.
1.3. html file compression
html webpack plugin is usually used to define html templates. You can also set the compress minify parameter (set true automatically in production mode)
1.4 picture compression
Using image webpack loader
2. Auto clean build directory
Use CleanWebpackPlugin to automatically clean the output directory specified by output
3. Static resource inline
The first screen rendering style should be inline or styled components. Resource inlining can reduce the number of requests, avoid the flashing of the first screen page, conduct relevant reporting and management, and initialize scripts
3.1 code level
- Raw loader: js/html inline
- Style loader: css inline
3.2. Request level
-
URL loader: small picture or font inline
-
File loader: it can parse the url import path in the project, modify the file reference path after packaging, and point to the output file.
4. Base library separation
4.1,HtmlWebpackExternalsPlugin
Pass the base package through cdn instead of compressing it into the bundle
plugins: [ new HtmlWebpackExternalsPlugin({ externals: [ { module: 'react', entry: '//11.url.cn/now/lib/15.1.0/react-with-addons.min.js?_bid=3123', global: 'React', }, ], }), ];
4.2,SplitChunksPlugin
You can separate public scripts, base packages, and page public files
splitChunks:{ chunks:'async',// async: separation of asynchronously imported libraries (default) initial: separation of synchronously imported libraries all: separation of all imported libraries (recommended) ... cacheGroups:{ // 1. Common script separation vendors:{ test:/[\\/]node_modules[\\/]/, priority:-10 }, // 2. Base package separation commons:{ test:/(react|react-dom)/, name:'vendors', chunks:'all' }, // 3. Page public file separation commons:{ name:'commons', chunks:'all', minChunks:2 } } }
4.3 subcontracting
plugins: [ // Subcontracting using DLLPlugin new webpack.DLLPlugin({ name: '[name]', path: './build/library/[name].json', }), // DllReferencePlugin to manifest JSON reference new webpack.DllReferencePlugin({ manifest: require('./build/library/manifest.json'), }), ];
5. Multi process and multi instance construction
Multi process and multi instance construction, in other words: each webpack parses a module and assigns it and its dependencies to worker threads, such as HappyPack and ThreadLoader
6. Cache
- Enable cache: Babel loader, terser webpack plugin
- Use cache loader, hard source webpack plugin
7. Narrow the building target and reduce the scope of file search
- Reasonably configure the test of the loader and use include to narrow the file processing range of the loader
module.exports = { module: { rules: [ { test: /\.js$/, // The tail supplementary $sign indicates tail matching use: ['babel-loader?cacheDirectory'], // Babel loader enables caching through the cacheDirectory option include: path.resolve(__dirname, 'src'), // Only the code in the src directory is processed, which greatly improves the compilation speed. (if there are uncompiled libraries under node_modules, it is not recommended to open them here) }, ], }, };
- Optimize the resolve configuration:
module.exports = { resolve: { modules: [path.resolve(__dirname, 'node_modules')], // The absolute path is used to indicate the storage location of the third-party module to reduce the search steps extensions: ['.js', '.json'], // extensions should be minimized to reduce the number of file searches noParse: [/\.min\.js$/], // noParse can ignore the dependency resolution of the module. The min.js file is generally packaged }, };
9, Maintainable webpack build configuration
1. Multiple profiles manage webpack configurations for different environments
1.1. Merge configuration through webpack merge
merge = require('webpack-merge'); module.exports = merge(baseConfig, devConfig);
2. Analysis of Web pack construction
2.1 log analysis
In package Add stats to the build statistics field of the JSON file
"scripts":{ "build:stats":"webpack --env production --json > stats.json" }
2.2 speed analysis
Use speedmeasure webpack plugin to analyze the total packaging time and the time consumption of each plug-in and loader
const speedMeasureWebpackPlugin = require("speed-measure-webpack-plugin") const smp = new speedMeasureWebpackPlugin() const webpackConfig = smp.wrap({ plugins:[ new MyPlugin() ... ] })
2.3 volume analysis
Use bundleAnalyzerPlugin to analyze the file size of the dependent third-party module and the component code size in the business. After construction, it will be displayed on port 8888
const bundleAnalyzerPlugin = require('webpack-bundle-analyzer'); module.exports = { plugins: [ new bundleAnalyzerPlugin({ analyzerMode: 'server', analyzerHost: 'localhost', analyzerPort: 8888, // Port number reportFilename: 'report.html', defaultSizes: 'parsed', openAnalyzer: true, generateStatsFile: false, // Output to static file statsFilename: 'stats.json', statsOptions: null, logLevel: 'info', }), ], };
2.4 compile time progress analysis
Using ProgressPlugin to analyze compilation progress and module processing details
const webpack = require('webpack'); module.exports = { plugins: [ new webpack.ProgressPlugin({ activeModules: false, entries: true, handler(percentage, message, ...args) { // Print real-time processing information console.info(percentage, message, ...args); }, modules: true, modulesCount: 5000, profile: false, dependencies: true, // Displays the dependency count message in progress dependenciesCount: 10000, percentBy: null, }), ], };