After some simple configuration of Webpack, the project can use React to write basic components. However, every time we write components, we need to re-execute the webpack command to package them, and then manually open the page in the browser to see the development result, which greatly affects the development efficiency. Yarn start start the project to run, during the development process use CTRL + S to save the file, then the page can be automatically updated, for this need to configure HMR and webpack-dev-server.

Development mode

Mode

If you want to use the development mode functionality, you need to specify the development mode for Webpack, because if you do not specify the mode when executing the webpack command, the default is production.

module.exports = {
  mode: 'development'};Copy the code

For different modes, WebPack uses built-in optimizations for corresponding modes, such as enabling some built-in plugins, as follows:

development

  • Register the global environment variable attribute process.env.node_env = “development” in DefinePlugin

  • Enable NamedChunksPlugin

  • Enable NamedModulesPlugin

production

  • inDefinePluginRegister global environment variable properties inprocess.env.NODE_ENV = "production"
  • To enable theFlagDependencyUsagePluginIs used to mark the current moduleimportPartially usedimport, you can delete the useless ones laterimport
  • Enable FlagIncludedChunksPlugin, which is used to add ids to other chunks introduced for each chunk and eliminate useless chunks
  • To enable theModuleConcatenationPluginTo improve code execution efficiency by connecting all modules into a closure function, this plugin is enabledtree shakingPlug-ins whose functionality must be enabled
  • To enable theNoEmitOnErrorsPluginIn case of compilation errorsNoEmitOnErrorsPluginTo skip the output phase. This ensures that the output resource does not contain errors
  • To enable theOccurrenceOrderPluginThis plugin used to be calledOccurenceOrderPluginBy assigning ids to modules by the number of module calls, commonly used IDS will be assigned shorter ids, making IDS predictable and reducing file sizes
  • To enable theSideEffectsFlagPlugin, side effect code tag, cooperatepackage.json"sideEffects"Configuration item use, for code with no side effects, you can remove some uselessexportexport
  • Enable TerserPlugin to compress JS code using Terser

Configuration for different modes

Function configuration

Webpack supports a variety of configuration types. Generally, simple configuration can be done by exporting a Webpack configuration object using module.exports. However, for complex business situations, such as enabling different loader, plugin and other functions for different environments, This need to use webpack function configuration to solve the problem.

The webpack function configuration accepts two parameters:

  • The first argument is aThe environment variableobjectenv, built-in environment variable properties, webpack command lineEnvironment configuration 的 --envParameter, which allows you to pass in any number of environment variables. And in thewebpack.config.jsYou can access these environment variables in
// Run the webpack command and specify the production environment and configure the environment variable NODE_ENV
webpack --env.NODE_ENV=development --progress
Copy the code
  • The second argument is a map object (argv), can be accessed fromargvGets the configuration item information in the webPack configuration
// webpack.config.js
module.exports = function(env, argv) {
  // Accept the NODE_ENV environment variable
  const NODE_ENV = env.NODE_ENV;

  return {
    // Determine the environment mode specified for Webpack when executing the command
    mode: env.production ? 'production' : 'development'.plugins: [
      new webpack.optimize.UglifyJsPlugin({
        // Pass arguments through argv
        compress: argv['optimize-minimize']]}}); };Copy the code

Different configuration files

If you don’t want to in a webpack. Config. Mix all the configuration items in js, for the purpose of higher maintainability, can use different webpack configuration files, such as development environment specified webpack. Development. Config. Js, The command line interface parameter –config specifies a different configuration file to use when executing the webpack command.

// Specify the configuration file to be used. Config follows the configuration file path
webpack --config webpack.config.js

/ / specified in the config folder webpack. Development. Config. Js file
webpack --config config/webpack.development.config.js
Copy the code

Switch between different modes

If the mode configuration item of webpack.config.js is written directly, there are two ways to switch freely between different modes of Webpack:

  • The first is executionwebpackCommand when provided through WebpackCommand line interfaceparameter--modeThe specified mode
// Specify the development mode
webpack --mode=development

// Specify the development mode
webpack --mode=production
Copy the code
  • The second way is also through the command-line interface arguments provided by WebPack--modeSpecifies the mode, but combines itnpm-scriptsinpackage.jsonConfigure the command in the file so that it can passnpm run xxxoryarn xxxTo specify the mode for Webpack
{..."scripts": {
    "start": "webpack --mode=development"."build": "webpack --mode=production"}},Copy the code
  • The last one is recommended. As mentioned above, WebPack supports function configurations that accept command-line interface arguments--envEnvironment variable object, so this approach is passed to the Webpack configuration function by configuring the startup command in npm-scriptsenvThe environment object is then passed inside the functionenvDetermine the current mode and write tomodeIn the configuration items
// Package. json scripts command configuration{..."scripts": {
    "start": "webpack --env.NODE_ENV=development"."build": "webpack --env.NODE_ENV=production"}},Copy the code
module.exports = function(env){
  const isDevelopment = env.NODE_ENV === "development";
  const isProduction = env.NODE_ENV === "production";
  return {
    mode: isProduction ? "production" : isDevelopment && "development". }}Copy the code

WDS

Use the webpack – dev server. –

WDS, Webpack-dev-server, Webpack development server. According to Webpack, webpack-dev-server can start a simple web server locally with live reloading, and can also use WebPack’s watch mode to automatically build file changes. However, the view mode cannot automatically refresh the page in the browser. You need to manually refresh the browser to see the actual effect after modification.

Webpack-dev-server automatically packs files and refreshes browser pages.

First, install webpack-dev-server

yarn add webpack-dev-server -D
Copy the code

Simply enable webpack-dev-server in webpack.config.js

module.exports = {
  ...
  devServer: {
    open: true.port: 9999.compress: true.writeToDisk: false,}}Copy the code

Also modify the start configuration in npm-scripts

{..."scripts": {
    "start": "webpack-dev-server --env.NODE_ENV=development". }},Copy the code

To see the effect of webpack-dev-server, run the yarn start command.

Next, test modifying the auto-refresh page

Webpack-dev-server can only be used on the command line, not in webpack.config.js. See — configuration-devserver.

Configuration items Value types default meaning
port number 8000 Domain name port number
compress boolean false Turn on gzip compression for each static file
https boolean false If HTTPS is enabled, a customized certificate is required. Otherwise, the browser displays an error message
open boolean false Tell dev-server to display the page in the system default browser after the server starts
openPage string index.html Specifies the page to view when the browser opens. The default is the root directoryindex.html
proxy object null This configuration is quite useful and can be used directly to solve cross-domain requests in the development environment without using the server, as documented in –http-proxy-middleware
contentBase boolean false Specifies the path to the static file provided to devServer
watchContentBase boolean false Listen [devServer.contentBase] option provides a static file. When enabled, file changes trigger a whole page reload
writeToDisk boolean false telldevServerWrite the automatically packed file to the hard diskwebpack.config.jsConfigured inoutput.path
stats string usingThe stats object, controls what information is displayed in the console, for example'errors-only'Output only when an error occurs or a new compile is available;

With the followingnoInfoorquietThis option is invalid when used together.
overlay object false Compiler errors or warnings are displayed in full screen in the browser
noInfo boolean false Hide information about the Webpack process in the terminal and only show it when there is an error or warning
quiet boolean false Hide webpack errors or warnings in the terminal and only display the initial startup information in the console
liveReload boolean true Hot reloading, where by default devser recompiles the package and then refreshes the page when a file change is detected; This option is automatically disabled if HMR is enabled
inline boolean true inlineMode automatically refreshes the page, and build messages appear in the browser console if set tofalse, then the page content will be placed in aiframeDebug, and a debug progress bar will appear at the top of the page, when the component is updated, you can’t see the page refresh process
hot boolean false Enable the HMR
hotOnly boolean false hotOnlyWill be a fallback if the build fails; If you havehotOnlySet totrueAfter the component is updated, the browser will not refresh automatically

HMR

Concept – HMR

By default, webpack-dev-server hot-loads liveReload. When a file changes are detected, Devser recompiles the package and refreshes the page.

For projects with small amount of code, the time loss of recompilation will not be too much, but the project needs to use more and more components, modules, etc., the packaging process will be slower and slower, which will affect the development efficiency more and more.

HMR, hot Module replacement, and module hot replacement are the functions provided by Webpack to update the page without reloading the whole page after replacing, adding and deleting module codes during the program running in the local development environment. At the same time, the current page state can also be saved.

Use HMR, do not need to install additional plugins, only need to enable hot: in devserver configuration items can be true, when set hot: true, will automatically add HotModuleReplacementPlugin this plug-in, without other configuration.

module.exports = {
  ...
  devServer: {
    hot: true}},Copy the code

After HMR is successfully enabled, the browser devtool typically outputs HMR information, for example

What React does in HMR is reintroduce the root Component and re-render it. Since the HMR is a hot replacement of the root Component, the state of both the root component and its internal components is lost after the replacement, but retained for external data containers such as the Redux Store.

React Fast Refresh

The React Hot Loader has been used by the community as a Hot update solution for React components, but in nineteen nineteen, the React Team ported the React Native Fast Refresh feature to support web development.

React Fast Refresh There Is no formal introduction to React Fast Refresh, and the concept only exists in the GitHub Issue discussion – What Is Fast Refresh?

The React Fast Refresh WebPack Plugin is already available in the community

yarn add @pmmmwh/react-refresh-webpack-plugin react-refresh -D
Copy the code

To use React Fast Refresh, you need to configure two steps in webpack.config.js:

  • Enable to introduce ReactRefreshWebpackPlugin;
  • inbabel-loaderenablereact-refresh/babelThis plugin;
/ / introduce ReactRefreshWebpackPlugin
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');

module.exports = function(env){
  const isDevelopment = env.NODE_ENV === "development";
  return {
    plugins: [
      ...
      isDevelopment && new ReactRefreshWebpackPlugin(),
    ].filter(Boolean),
    module: {
      rules: [{test: /\.m? jsx? $/,
          exclude: /(node_modules)/,
          use: {
            loader: "babel-loader".options: {...plugins: [
                ...
                isDevelopment && require.resolve("react-refresh/babel"),
              ].filter(Boolean),},},},],},}}Copy the code

Filter (Boolean) : the filter(callback) method of Array itself removes elements from the Array that do not meet the criteria, i.e., it only adds elements to the newly generated Array that call callback to return true. When the constructor Boolean is used as a callback, it will return true if the Boolean is not false, undefined, NaN, 0, or the empty string “”.

When using &&, plugins or loaders that are not used in the current environment will also return false. Filter (Boolean) is used to filter out plugins or loaders that are not in use in the current environment.

source map

What is a Source map

The original JS code will be minify to remove unnecessary whitespace, comments, line breaks, etc. from the code to reduce the size of the code. Because JS is embedded in HTML, it needs to be obtained through the network before it can be parsed and executed. Reducing the code volume is beneficial to shorten the network request time, and then indirectly shorten the loading time of web pages.

Then came the code obfuscation technology, by rewriting the names of various elements in the code, such as variables, functions, classes into meaningless names, to improve JS security in the client.

Although these operations are beneficial to improve the client experience, but not conducive to developer debugging, so the source Map technology emerged, the source code and compressed code corresponding, by opening source Map can be very good to find the mapped source code, so as to facilitate debugging.

Source map is essentially a JSON file with a. Map extension, which contains some mapping properties of the source file and compressed file, for example

{
  "version": 3./ / version
  "file": "script.js.map".//source map file name
  "sources": [
    // Source file name
    "app.js"."content.js"."widget.js"]."sourceRoot": "/".// Source file path
  "names": ["slideUp"."slideDown"."save"].// An array containing the names of all variables and functions in the source file
  "mappings": "AAA0B,kBAAhBA,QAAOC,SACjBD,OAAOC,OAAO..." //
}
Copy the code

From the Source Map file, write the path to the Source Map file at the bottom of the compressed code file with a comment field sourceMappingURL to tell the browser that the compressed file has a source file map available, for example

//# sourceMappingURL=/path/to/script.js.map
Copy the code

Later, languages derived from JS, such as JSX, TS, CoffeeScript, etc., can also be mapped using this technique. Some browsers also have built-in source map support. For example, Chrome can enable SOURCE Map for JS and CSS in the DevTool Settings panel.

React Fast Refresh (WDS) {React Fast Refresh (WDS) {React Fast Refresh (WDS) {

export default class extends Component {
  state = {
    value: ' '};/* An error is reported when typing */
  handleChange = e= > {
    throw new Error('test');
  };

  render() {
    return (
      <div>
        <input value={this.state.value} onChange={this.handleChange} />
      </div>); }}Copy the code

If you enter an error message, the following error message is displayed

However, it is still necessary to use source map in the production environment. For the above error test code, when we execute yarn Build package code, the generated directory is as follows:

├─ favicon.ico ├─ index.html ├─ main.jsCopy the code

Devtool displays an error message when an HTML page is opened in a browser.

As you can see, this error message points to a code location that is already confused. If it is a complex error, this code location is useless.

devtool

To enable source Map in webpack, all you need is a configuration item devtool, which you can pass in the specified pattern string or disable with devtool:false; If you omit the Devtool configuration item, the Source map file will not be generated.

Devtool

See devtool for 7 SourceMap patterns

module.exports = {
	...
  devtool: isProduction ? "source-map" : false};Copy the code

You are advised to use devtool “source-map” in the production environment. Run yarn Build to package the file again. A. Map file is found in the directory

├─ ├─ main.js └─ favicon.ico ├─ index.html ├─ main.jsCopy the code

At this time open the HTML page test input error, you can see that has successfully located the source code error point, OK!

In addition to webPack’s built-in source map output, some Loaders also provide configuration options to generate source maps, but they ultimately depend on whether the Devtool configuration item is enabled. For example, csS-loader provides the sourceMap configuration item to generate a source map for CSS files.

However, if devtool is configured in a production environment, the impact on build speed varies depending on the devtool. Generally speaking:

  • The specifieddevtool:source-mapThe location of the error message can be traced in detail, but errors can be traced directly to the source code in DevTool, as shown above
  • The specifieddevtool:evalIt is possible to display the error location, but the code will be compiled by Babel, etc. If the React component is used, it will also be able to analyze the error location

  • Specifying devtool:eval-cheap-source-map looks similar to devtool:eval, which is still compiled code

  • Specifying devtool:eval-cheap-module-source-map also shows the exact source location, but builds much faster than devtool:source-map, which is recommended by Webpack

  • Devtool :eval-source-map, eval-nosource-map, eval-nosource-map, eval-nosource-map Eval-nosource-cheap-module-source-map also displays source location

Pay attention to the point

If you customize the terser-webpack-plugin configuration in the webPack configuration item optimization.minimizer, even if you only write an instance that initializes the Terser-webpack-plugin, The sourceMap configuration item of terser-webpack-plugin must be enabled. If this is not configured and devtool is not specified, The code mapping of the development environment is mapped directly to the chunk generated by the package, not to the source code.

const TerserPlugin = require('terser-webpack-plugin'); // Compress the JS code

module.exports = {
  optimization: {
    minimizer: [
      new TerserPlugin({
        sourceMap: true,}),],},};Copy the code