foreplay
Hi~ o(* ̄▽ ̄*)ブ ブ this is the last piece of Webpack5 build React nice development environment. Because the last article is mainly used to test the water may be relatively shallow, the details are not considered in place, so through this article to improve it. Dry goods or can be harvested after reading, it is recommended to drink water while watching. I’ve been busy with burying things lately, and I didn’t want to coop, so I wrote it over the weekend. Ok, so let’s get down to business, and post an article portal so you can review it, and the full source code
This process planning
Graph TD development environment configuration improvement --> production environment configuration improvement --> optimization ideas
Perfect development and configuration
Babel translator and plug-ins
First, complete the previous Loader configuration
Here is the original loader configuration for js and JSX
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader".options: {
presets: [
"@babel/preset-env"."@babel/preset-react",],},},],},Copy the code
Babel presets configuration
Babel is an official definition of a JavaScript compiler. It is used to translate the ES syntax and APIS into code that can be run by existing browsers.
The installation
babel-loader @babel/core @babel/preset-env
Copy the code
These three count as an essential presence in webpack
babel-loader
This package allows JavaScript file babel-loader to be compiled using Babel and Webpack
@babel/core
It is the core Babel library that provides many apis for translating source files, and it requires plug-ins so that the translation itself does not translate
import { transformSync } from "@babel/core";
function babelLoader(source, options) {
// var options= {
// presets: [
// "@babel/preset-env",
// "@babel/preset-react",
/ /,
/ /},
var result = transformSync(source, options);
return this.callback(null, result.code, result.map, result.ast);
}
module.exports = babelLoader;
Copy the code
source
The required translation source file or the result of the previous loader translationoptions
Configure the options parameter passed by the loadertransformSync
Synchronous translation of incoming code, return after translationcode
,sourceMap
Mapping andAST
Object.
@babel/preset-env
Babel /preset-env is a grammar translator or preset, but it only converts new ES syntax. Instead of converting the new ES apis, such as Iterator, Generator, Set, Maps, Proxy, Reflect,Symbol,Promise, the new apis can be translated via babel-profill, Let the browser implement the functionality of the new API but babek-profill is no longer recommended and core-js is recommended
⚠️ As of Babel 7.4.0, this package has been deprecated in favor of directly including core-js/stable (to polyfill ECMAScript features) and regenerator-runtime/runtime (needed to use transpiled generator functions):
npm i core-js -S
Copy the code
Configuration is as follows
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader'.options: {
presets: [['@babel/preset-env', {
useBuiltIns: 'entry'.corejs: '3.9.1'.targets: {
chrome: '60',}}],'@babel/preset-react'],},},],},Copy the code
@ Babel/preset – env parameter
-
UseBuiltIns: “usage” | | “entry” false, the default is false, there something about the usage of other parameters specific see the official description of the portal
-
Usage will polyfill only the API you are using based on the browser compatibility configured, adding patches as needed
-
The targets:
// Compatible with market share >0.25%
{
"targets": "> 0.25%, not dead." "
}
// Make compatible with objects in the lowest version of the environment to support
{
"targets": {
"chrome": "58"."ie": "11"}}Copy the code
When no target is specified, it behaves similarly: Preset -env converts all ES2015-ES2020 code to ES5 compatibility. It is not recommended to use the following preset-env directly because it does not take advantage of the environment/version specific features
{
"presets": ["@babel/preset-env"]}Copy the code
Deprecated since @babel/ Polyfill7.4.0, it is recommended that you add and set versions of core-js directly with this corejs option
- Corejs: ‘3.9.1’ This ‘3.9.1’ is the core-JS version number
@babel/preset-react
React Babel is called by JSX to React. CreateElement (), which is used to translate React code.
- This is a piece of JSX code
<div className="wrap" style={{ color: "# 272822"}} ><span>Study together</span>React
</div>
Copy the code
- Preset /preset/react
React.createElement(
"div",
{
className: "wrap".style: {
color: "# 272822",
},
},
React.createElement("span".null."Learning together"),
"React"
);
Copy the code
Babel plugin configuration
- @babel/plugin-syntax-dynamic-import supports dynamic import loading, and @babel/preset-env does not support dynamic import syntax translation.
Currently, @babel/preset-env is unaware that using import() with Webpack relies on Promise internally. Environments which do not have builtin support for Promise, like Internet Explorer, will require both the promise and iterator polyfills be added manually.
- @babel/plugin-proposal-decorators compile class and object decorators into ES5 code
- @babel/plugin-proposal-class-properties Transforms static class properties and initializes properties declared by syntax using properties
Configure the plug-ins required for translation. The order in which plug-ins are used is the order in which they are called in the array
Now the babel-loader parameter is bloated and can be mentioned in the.babelrc.js file
module.exports = {
presets: [["@babel/preset-env",
{
useBuiltIns: "entry".corejs: "3.9.1".targets: {
chrome: "58".ie: "11",},},], ["@babel/preset-react",
{
development: process.env.NODE_ENV === "development",}]].plugins: [["@babel/plugin-proposal-decorators", { legacy: true }],
["@babel/plugin-proposal-class-properties", { loose: true}]."@babel/plugin-syntax-dynamic-import",]};Copy the code
Eslint configuration
The eslint-webpack-plugin is currently recommended for esList, as eslint-Loader will soon be obsolete
⚠️ The loader eslint-loader will be deprecated soon
The installation
npm i
eslint
eslint-webpack-plugin
eslint-config-airbnb-base
eslint-plugin-import -D
Copy the code
-
Eslint >= 7 (version)
-
Eslint-config-airbnb-base supports all ES6 + syntax specifications and requires esLint to be used in conjunction with eslint-plugin-import
-
Eslint-plugin-import is used to support eslint-config-airbnb-base for import/export syntax checking
webpack.dev.js
new ESLintPlugin({
fix: true.// Enable ESLint automatic repair
extensions: ['js'.'jsx'].context: paths.appSrc, // File root directory
exclude: '/node_modules/'.// Specify files/directories to exclude
cache: true./ / cache
}),
Copy the code
React JSX also needs to react JSX.
npm i eslint-plugin-react -D
// Configure react in the esLint config extension default
extends: [
"plugin:react/recommended".// JSX specification support
"airbnb-base".// contains the desired ES6+ specification].// Or set it in the plugin
"plugins": [
"react"
]
Copy the code
Also configure the.eslintrc.js file in the root directory
.eslintrc.js
module.exports = {
env: {
browser: true.es2021: true,},extends: [
"airbnb-base".// contains the desired ES6+ specification
"plugin:react/recommended".// React JSX specification support].parserOptions: {
ecmaFeatures: {
jsx: true,},ecmaVersion: 12.sourceType: "module",},plugins: [].rules: {
"consistent-return": 0.// Arrow functions do not force return
semi: 0."no-debugger": process.env.NODE_ENV === "production" ? "error" : "off"."react/jsx-uses-react": "error".// Prevent react from being wrongly marked as unused
"react/jsx-uses-vars": "error"."react/jsx-filename-extension": [1, { extensions: [".js".".jsx"]}],"react/jsx-key": 2.// Verify that JSX has the key attribute in an array or iterator
"import/no-dynamic-require": 0."import/no-extraneous-dependencies": 0."import/no-named-as-default": 0.// 'import/no-unresolved': 2,
"import/no-webpack-loader-syntax": 0."import/prefer-default-export": 0."arrow-body-style": [2."as-needed"].// Arrow function
"class-methods-use-this": 0.// Force class methods to use this
// Indent with 4 Spaces
indent: ["error".4, { SwitchCase: 1}].// SwitchCase conflict flicker problem
// Indent JSX with 4 spaces
"react/jsx-indent": ["error".4].// Indent props with 4 spaces
"react/jsx-indent-props": ["error".4]."no-console": 0.// Stop using console
"react/jsx-props-no-spreading": 0."import/no-unresolved": [
2,
{
ignore: ["^ @ /"].// @ is the path alias},],},// Eslint-import-resolver-webpack is required if an alias is configured in webpack.config.js and an alias is used when importing
settings: {
"import/resolve": {
webpack: {
config: "config/webpack.dev.js",},},},};/* "off" or 0 // Close the rule close "WARN" or 1 // open the rule as a warning (does not affect the exit code) "error" or 2 // treat the rule as an error (1 when the exit code is triggered) */
Copy the code
You could also put the ESLint configuration in package.json, as follows but it’s a bit too much to put in the root directory to reduce coupling
"eslintConfig": {
"extends": ["plugin:react/recommended"."airbnb-base"],... Omit}Copy the code
Intellisense import Alias import file
By default, alias is configured in Vscode via webpack.resolve.alias. There is no path for import
jsconfig.json
{
"compilerOptions": {
"baseUrl": "./src".// Base directory for resolving non-relative module names
"paths": {
"@ / *": [". / *"] // Specify the path map to calculate the alias relative to the baseUrl option
},
"experimentalDecorators": true Provide experimental support for the ES decorator proposal
},
"exclude": ["node_module"]}Copy the code
This alias should match the alias in WebPack Resolve
webpack.common.js
resolve: {
modules: [paths.appNodeModules],
extensions: ['.js'.'.jsx'.The '*'].mainFields: ['browser'.'jsnext:main'.'main'].alias: {
moment$: 'moment/moment.js'.'@/src': paths.appSrc,
},
},
Copy the code
Effect:
Complete environment configuration
Environment variable configuration can be divided into node environment configuration and module environment configuration, both of which are separate Settings and cannot be shared
Node global variable
Cross-env allows you to set global variables in the Node environment to distinguish between development and production mode
⚠️ is invalid under the ESM
npm i cross-env -D
Copy the code
package.json
"scripts": {
"build": "cross-env NODE_ENV=production webpack --config config/webpack.prod.js"."start": "cross-env NODE_ENV=development node server". Omit},Copy the code
.eslintrc.js
Once you have configured the node environment global variables, you can obtain the values from process.env.node_env
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'.// The debugger is not allowed in a production environment
Copy the code
DefinePlugin is used to set global variables within a module
This is a plugin that comes with Webpack and can be obtained from process.env.node_env in any module
new webpack.DefinePlugin({
NODE_ENV: isEnvProduction && JSON.stringify('production'), // Set global
}),
Copy the code
const App = () = > {
console.log(process.env.NODE_ENV); // development
return (
<div>
<Index />
1133366
</div>
);
};
Copy the code
IgnorePlugin
IgnorePlugin
Used to ignore specific modules so that WebPack does not pack them
new webpack.IgnorePlugin(/^\.\/locale/./moment$/);
Copy the code
The production environment is well configured
Out of the CSS
The default packaging is to inject styles into JS files and add runtimes to the Head style tag. This is convenient in development mode, but production environments recommend separating CSS into separate files, so that if the application code changes, the browser can only get the changed JS files. Extracted CSS files can be cached separately.
The installation
npm i -D mini-css-extract-plugin
Copy the code
-
The plug-in extracts CSS into a separate file. It creates a CSS file for each JS file that contains CSS, which cannot be used with style-loader, so let it work in production mode.
-
Add loader and Plugin to the WebPack configuration file
webpack.commom.js
const isEnvProduction = options.mode === 'production';
{
test: sassRegex,
exclude: sassModuleRegex,
use: [
isEnvProduction ? MiniCssExtractPlugin.loader : 'style-loader',
{
loader: 'css-loader'.options: {
importLoaders: 1,}},'postcss-loader'.'sass-loader',]},Copy the code
webpack.prod.js
plugins[... ellipsisnew MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:8].css'.// The name of the output CSS file
chunkFilename: 'css/[name].[contenthash:8].chunk.css'.// Non-entry CSS Chunk file name
ignoreOrder: true.// Ignore warnings about order conflicts}),].Copy the code
Compress CSS
Optimize CSS Assets Webpack Plugin with Webpack 5 Optimize CSS Assets Webpack Plugin with CSS -minimizer-webpack-plugin
⚠️ For webpack v5 or above please use css-minimizer-webpack-plugin instead.
This plugin uses CSSNano to optimize and compress CSS. It’s like the optimize-CSS-assets-webpack-plugin, but using query strings in Source maps and Assets is more accurate, enabling caching and concurrent mode.
The installation
npm install image-webpack-loader --save-dev
Copy the code
This will only enable CSS compression optimization in production mode, with the optimization.minimize option set to true if needed in development mode
webpack.common.js
optimization: {
minimize: isEnvProduction, // Whether it is a production environment
minimizer: [
new CssMinimizerPlugin({
parallel: true.// Enable concurrent execution of multiple processes, default os.cpus().length-1
}),
new TerserPlugin()
],
},
Copy the code
After the compression
Compression js
Terser-webpack-plugin uses Terser’s JavaScript parser for ES6+ to compress JS files. Webpack4 is built into WebPack 5 and needs to be installed separately
webpack.common.js
const TerserPlugin = require('terser-webpack-plugin')...optimization: {
minimize: isEnvProduction,
minimizer: [...new TerserPlugin({
parallel: true.// Enable concurrent execution of multiple processes})],},Copy the code
The compressed image
Compressed pictures are also an important part of packaging optimization at ordinary times
Image-webpack-loader can help compress and optimize images, but Cannot find module ‘gifsicle ‘Cannot find module ‘gifsicle’ Cannot find module ‘gifsicle Image-minimizer-webpack-plugin also does not install the GIF plugin, but it can be installed on scientific Web
image-webpack-loader
The installation
npm i -D image-webpack-loader
Copy the code
Image-webpack-loader Specifies the Issues corresponding to the installation
CNPM is downloadable but CNPM is not a standard download tool relative to Webpack 5 rules or try using image-Webpack-loader lower version, I tried yarn and NPM and both encountered Cannot find module ‘gifsicle’
If you have successfully used image-webpack-loader, you can use the following configuration
{
test: /\.(gif|png|jpe? g|svg|webp)$/i,
type: 'asset'.parser: {
dataUrlCondition: {
maxSize: imageInlineSizeLimit, // 4kb}},use: [{loader: 'image-webpack-loader'.options: {
mozjpeg: {
progressive: true.quality: 65,},optipng: {
enabled: false,},pngquant: {
quality: '65-90'.speed: 4,},gifsicle: {
interlaced: false,},webp: {
quality: 75,},},},],},Copy the code
- Mozjpeg – Compress JPEG images
- Optipng – Compress PNG images
- Pngquant – Compressed PNG images
- Svgo – Compress SVG images
- Gifsicle – Compressed GIF images
Before compression
After the compression
image-minimizer-webpack-plugin
Choose one of these two and you can install it
npm install image-minimizer-webpack-plugin --save-dev
Copy the code
Images can be optimized in two modes:
- Non-destructive (no mass loss)
- Lossy (loss of mass)
The specific selection of official documents has been given
The Imagemin plug-in is optimized nondestructively
npm install imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo --save-dev
Copy the code
The Imagemin plug-in is used for lossy optimization
npm install imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo --save-dev
Copy the code
Imagemin-gifsicle doesn’t install either,
The following configuration can be used if the installation is successful
webpack.prod.js
new ImageMinimizerPlugin({
minimizerOptions: {
plugins: [['gifsicle', { interlaced: true }],
['jpegtran', { progressive: true }],
['optipng', { optimizationLevel: 5 }],
[
'svgo',
{
plugins: [{removeViewBox: false,},],},],],},})Copy the code
The idea of optimization
The optimization direction is divided into two: one is time (speed) and space (volume) Webpack. No matter the module specification is packaged and unified, it is webpack_require. If the development mode can support ESM scheme, it will be good.
Now this kind of optimization means is also more, or you can try other tools Vite, Snowpack, but the stage is still mature, really used in the project may still be relatively few, pit more, I also used Vite for a few pages in a small project, but the tool itself is really nice, looking forward to its development. Webpack optimization I will mention the idea, and then a lot of blogs have mentioned, you can go to see their.
No matter what you do, you need to have a clear goal, and optimization is the same. You need to know where it takes the most time, and you need to focus on what you can do. First, the speed-measure-webpack-plugin can analyze the time for each step of packaging
I failed to use this package, and some people have encountered this problem in the issue
npm i speed-measure-webpack-plugin -D
Copy the code
Webpack-bundle-analyzer analyzes what files are packaged, what is the size ratio, module inclusion relationships, dependencies, whether files are duplicated, and how big are the compressed files
npm i webpack-bundle-analyzer -D
Copy the code
Compile time optimization
Narrow down the file search
resolve
resolve: {
modules: [paths.appNodeModules],
extensions: ['.js'.'.jsx'.The '*'].mainFields: ['browser'.'jsnext:main'.'main'].alias: {
moment$: 'moment/moment.js'.'@/src': paths.appSrc,
},
},
Copy the code
rule.oneOf
If oneOf can be used to solve this problem, you can exit as long as there is a match. Similar to array. find, if it finds the right one, it will return and will not continue to search
module.exports = {
module: {
rules: [{oneOf: [...,]}]}}Copy the code
-
external
-
Multiprocess processing
The cache
Persistent cache
babel-loader
Open the cache
Volume optimization
-
CSS HTML Image JS compression
-
Tree-sharking removes useless code
In the Webpack4 version, it is easier to remove unnecessary code when packaging. The main thing is to find out if an imported variable appears in the module, and Webpack5 can optimize it based on the relationship between scopes
Tree-shaing relies on ESM, and if it is not ESM, it will not be supported, such as CommonJS, mainly because EMS is static dependency analysis and compilation can determine its dependencies, while require is running or loading, do not know how it depends on the painful.
-
SplitChunks subcontract
The problem
There was a problem with webpack-dev-server in the last post, which is that the page failed to refresh after the error was reported. I tried it and it did exist. I can’t find the exact reason for this either, but esLint is configured to restore hot updates when it is recompiled, so try it out.
conclusion
So here the whole construction is over ^_^ there are some knowledge points I may not write, the specific details can be source code. And this should be the last webpack post. Some interesting Webpack plug-ins might be written later. If you encounter problems debugging Webpack5, please discuss them in the comments section. Finally, I hope you all have a harvest, see you next…