Configuration of source map in webpack

Posted by rougue on Sun, 06 Mar 2022 04:42:27 +0100

Know source map

Generally, the code actually running on the browser is the code packaged by front-end construction tools such as webpack. In the packaging process, the code will be compressed, confused and vilified, so this will lead to the difference between the code running on the browser and the source code we wrote in the development stage, which is mainly reflected in the following aspects:

  1. The syntax of ES6 + in the source code will be transformed into ES5 syntax through babel tool;
  2. The code line number and column number in the source code will be inconsistent with the browser after compilation and packaging;
  3. Variable names and function names in the source code will be abbreviated and replaced after compression and vilification;
  4. If TS development is adopted in the source code, it will also be converted into JS code and executed in the browser

In fact, the above problems will eventually be reflected in a problem: that is, when the code runs on the browser side, it is extremely difficult for us to debug errors in the browser's devtools, because no one can guarantee that the self-developed code will not make mistakes!

So how do we debug the inconsistent code in the browser? The answer is source map. Source-map is a plan to map the packaged and compiled code to the original source file. After configuring source-map, we can accurately locate the code error in the source file when we debug the code in the browser.

Using source map

The premise of using source map is that the browser must support source map files, that is, the browser can read and parse bundles js. Map is a file. Using source map requires two steps:

  1. Configure the webpack packaging option devtool to generate a source map
module.exports = {
	devtool:"source-map",
}
  1. notes
    The above configuration represents that the JS file generated after final packaging will not only have a bundle JS file, there will also be a bundle js. Map file, and in the bundle There will be a line of comments at the bottom of the JS file:
//# sourceMappingURL=bundle.js.map

This line of code tells the browser to load the bundle JS file, download the bundle corresponding to the file according to the file path pointed to by the sourcemapping URL js. Map file, and then the browser can be based on bundle js. The information in the map file plus bundle The code in JS restores the source code before packing, which is what makes it possible to locate the wrong information in the source code precisely when debugging in the browser, because the source-map is configured and the bundle. generated is. js. The map file provides the browser with the necessary information to restore the source code file.

View the restored source file in the browser

After configuring the source map, if we want to view the source file before packaging in the browser, we also need to open: Enable JavaScript source maps in the devtools settings Sources option of the browser.

Compare the differences of the sources option in devtools before and after configuring source map:

  1. Devtool: source map is not configured
    127.0.0.1:8848 a bundle is saved on the local server JS file

  2. Configure devtool: source map

    In addition to the 127.0.0.1:8848 local server saving the packaged files, the browser also has a folder to save the current bundle All corresponding source code files in JS, in addition to those of the project itself, will be restored to the files in the webpack source code.

Analyze the generated source map file

At first, the file generated by source map is 10 times the size of the original file. Later, after two versions of optimization, it can only be about 2.5 times the size of the original file. Therefore, the final size of a 133kb source file should be about 300kd.

The source map file stores a series of information about how to map the source file through the packaged code in the form of object key value pairs, with the following seven attributes:

{ 
	/* Current source map version */
	"version": 3, 
	
	/* The packaged file currently loaded by the browser */
	"file": "js/bundle.js",
	
	/* 
	source-map The information used to map with the source file is composed of a string of base64 VLQ(Veribale length quantity) variable length value codes, and the location information of the code is determined based on this information
	 */
	"mappings": ";;;;;;;;;AAAA;AACA;AACA;....",
	
	/* Which source files are currently running in the browser converted from? Each item in the array corresponds to a source file path */
	"sources": [
	    "webpack://webpack-demo/./src/js/CommonJS.js",
	    "webpack://webpack-demo/./src/js/ESModule.js",
		"webpack://webpack-demo/webpack/bootstrap",
	],
	
	/* The source code information before packaging, and convert the source code into a string corresponding to sources */
	"sourcesContent": [
	  "function CommonSum (a,b) {\r\n\treturn a+b;\r\n}\r\n\r\nfunction CommonMul (a,b){\r\n\treturn a*b;\r\n}\r\n\r\nconsole.log(foo);\r\n\r\nmodule.exports = {\r\n\tCommonSum,\r\n\tCommonMul\r\n}",
	  "function ESModuleSum (a,b) {\r\n\treturn a+b;\r\n}\r\n\r\nfunction ESModuleMul (a,b){\r\n\treturn a*b;\r\n}\r\n\r\nconst ESModulec = 100;\r\n\r\n/* Based on ES Module syntax */\r\nexport default {\r\n\tESModuleSum,\r\n\tESModuleMul,\r\n\tESModulec\r\n}\r\n\r\n",
	],
	
	/* If it is in the production mode, the names of the converted variables will be confused. Names can restore the variable names in the source code before packaging; If the development pattern is an empty array
	 */
  "names": [
		"console",
        "log",
        "foo",
        "module",
        "exports",
        "CommonSum",
        "a",
        "b",
        "CommonMul",
	],
	
	/* The root directory of the source file path declared in all sources */
  "sourceRoot": ""
}

The devtool option based on webpack configures different source maps

The devtool configuration in the webpack configuration is used to control whether and how to generate the final source map. There are 26 values about devtool in the official document of webpack 5. Different values will lead to different contents of the generated source map and different performance in the packaging process of webpack, Therefore, we should flexibly configure devtool based on different environments and different project requirements, so as to control the generation of the best source map.

1. Set not to generate source map

1. Boolean value: false

Configuring devtool to false means that source map is not used, and there is no source map related content in the final packaged file. Note that this is a Boolean value. Do not write it as devtool:"false", which will lead to packaging failure. It is recommended to use this value to set that the source map is not generated when the project is packaged.

2. Default value of devtool in production mode: "none"

If the mode is set to production mode, none is the default value of devtool, that is, as long as the current mode is production mode, the source map will not be generated by default.

  • Note 1: if the mode is set to the development mode, the value of devtool cannot be set to none, because this value can only be used in production mode.

  • Note 2: if the mode is set to production mode, the default is none without setting any devtool option. At this time, if you explicitly set it, an error will be reported!!!

Therefore, the following two setting methods will report errors:

module.exports = {
	mode:"production",
	devtool:"none",
}

module.exports = {
	mode:"development",
	devtool:"none",
}

The correct setting method is to only declare mode as production and make the default configuration effective.

module.exports = {
	mode:"production", // In the production environment, source map is not produced by default
}

3. Default value of devtool in development mode: "eval"

If the current mode is set to the development mode, the default value of devtool is the string eval. At this time, packaging will not generate a source map. Although setting it to eval will not generate a source map file, when packaging a module, webpack will do the following compared with configuring a source map:

  • Convert the interface to be exported by the module in the source code into a code string and execute it in the eval() function. Because the eval function can execute the parameter string as js code.

  • Add a line of comment on the last side of the code string executed by eval: / / # sourceURL=webpack://webpack-demo/./src/js/CommonJS.js? When the browser parses this line of comment, it will generate the current bundle in the corresponding debugging panel The file directory in the corresponding source code in JS is convenient for us to debug the code in development mode.

  • The packaging and construction speed in eval mode is very fast. Generally, devtool can be set as eval in development mode.

/* Configure devtool: source map after packaging__ webpack_modules__ object */
	var __webpack_modules__ = {
	 "./src/js/CommonJS.js":
		 function(module) {

			function CommonSum (a,b) {
				return a+b;
			}
			function CommonMul (a,b){
				return a*b;
			}
			console.log(foo);

			module.exports = {
				CommonSum,
				CommonMul
			}
		}
	}
	
/* Configure devtool: after Eval is packaged__ webpack_modules__ object */
	var __webpack_modules__ = {
	"./src/js/CommonJS.js":
		function(module) {
			/* Convert the original exported functions and variables into strings and execute them in the eval function; And the code string in the eval function is translated and mapped to the file specified by the sourceURL to simply restore the source code
			 */
			eval(
				"function CommonSum (a,b) {\r\n\treturn a+b;\r\n}\r\n\r\nfunction CommonMul (a,b){\r\n\treturn a*b;\r\n}\r\n\r\nconsole.log(foo);\r\n\r\nmodule.exports = {\r\n\tCommonSum,\r\n\tCommonMul\r\n}\n\n//# sourceURL=webpack://webpack-demo/./src/js/CommonJS.js?");
		},
	}

2. Set and generate source map

Setting the value of devtool to "source map" can generate a separate source map file in the final packaged file, which is described above and will not be repeated here.

3. eval-source-map

Setting the value of devtool to "Eval source map" can generate a source map, but the generated source map is not a separate file, but is added to the back of the eval function in the form of DataUrl.

The key is / / # sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxl...\n / / this line of comment indicates that the browser is loading the bundle JS, the eval function will be executed, and the source map will be loaded based on the base64 resource pointed to by the sourcemapping URL.

// Package the generated bundle JS file
var __webpack_modules__ = {
	"./src/js/CommonJS.js":
		function(module) {
			/* Convert the original exported functions and variables into strings and execute them in the eval function; And the code string in the eval function is translated and mapped to the file specified by the sourceURL to simply restore the source code
			 */
			eval("function CommonSum (a,b) {\r\n\treturn a+b;\r\n}\r\n\r\nfunction CommonMul (a,b){\r\n\treturn a*b;\r\n}\r\n\r\nconsole.log(foo);\r\n\r\nmodule.exports = {\r\n\tCommonSum,\r\n\tCommonMul\r\n}//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxl...\n//# sourceURL=webpack-internal:///./src/js/CommonJS.js\n");
		},
	}

4. inline-source-map

Setting the value of devtool to "inline source map" can also generate source map, and there will be no separate Map file, but add the source map to the bundle in the form of DataUrl JS file.

// bundle.js file

//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoianMvYnVuZGxlLmpzIiwibWFwcGluZ3MiOiI7Ozs7Ozs7OztBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7OztBQ2JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSwrREFBZTtBQUNmO0FBQ0E7QUFDQTtBQUNBLENBQUM7QUFDRDs7Oz

5. cheap-source-map

Setting the value of devtool to "soap source map" will generate a separate source map file, and it will be more efficient than source map, because soap means low overhead. The reason why it is efficient is that there is no column mapping in the source map file generated in the soap source map mode, which means that if there is an error in the code, You can only locate the number of rows reporting errors, but you can't locate the specific number of columns of the row reporting errors. However, generally, you can locate only rows in development.

  • Use the default configuration of eval in development mode to locate the error column

  • Use the soap source map to locate the error to the row

6. cheap-module-source-map

Setting the value of devtool to "soap module source map" will generate a separate source map file. The specific behavior is similar to the soap source map. The only difference is that for the source file processed by the loader, the generated source map will be better and closer to the source code.

If the following ES6 + syntax code is processed into low version syntax by Babel laoder, the variable name and line number of the source map generated by the soap source map are different from the source file when locating errors, which is not very friendly for debugging errors.

  • Real source file: ES6 syntax such as const and arrow function is used, and bable loader is configured
const CommonSum = (a,b)=>{
return a+b;
}
const CommonMul = (a,b)=>{
	return a*b;
}
console.log(foo);
module.exports = {
	CommonSum,
	CommonMul
}
  • The source file finally restored from the source map generated by the soap source map is as follows: it can be seen that since the es6 + syntax has been processed by Babel loader, the source code has been transformed into ES5 syntax that can be adapted to lower version browsers. At this time, when positioning errors, there will be a problem that the code line number is inconsistent with that in the real source file.
var CommonSum = function CommonSum(a, b) {
  return a + b;
};

var CommonMul = function CommonMul(a, b) {
  return a * b;
};

console.log(foo);
module.exports = {
  CommonSum: CommonSum,
  CommonMul: CommonMul
};

The above problem can be solved by configuring the value of devtool as heap module source map. For example, the js code in the source file has changed after being processed by TS loader and Babel loader. Before packaging, configure it as heap module source map to ensure that the source file restored by the generated source map is consistent with the real source file without line number The difference between underlined variable names is actually the difference between the two configurations of soap module source map and soap source map, that is, better processing of the files processed by the loader.

  • The source files as like as two peas that are generated by cheap-module-source-map are the following: the source-map can be seen as identical as the real source file, and there is no difference between them, and the packaging performance is relatively high.
const CommonSum = (a,b)=>{
	return a+b;
}

const CommonMul = (a,b)=>{
	return a*b;
}

console.log(foo);

module.exports = {
	CommonSum,
	CommonMul
}

7. hidden-source-map

Setting the value of devtool to "hidden source map" will generate a separate source map file, but in budnle There will be no reference comments to the source map file at the bottom of JS, which is equivalent to deleting the reference comments to the source map file in the packaging file.
The difference between hidden source map and false lies in whether a separate source map file is generated. Although hidden source map will JS, but if we add the annotation manually, the source map will take effect and map out the source file.

/* In the bundle JS add the following comments */
//# sourceMappingURL=bundle.js.map

8. Nosources source map value

Setting the value of devtool to "nosources source map" will generate a separate source map file, but the generated source map can only be used to prompt errors and will not map the source code file, so it is impossible to locate the specific error information and view the source code.
Click the error message to jump to the source page and report an error:

Combination of different values when configuring the devtool option

webpack provides 26 values for the devtool option to configure whether and how to generate a source map. In addition to being defined separately, it can also be combined, but it should be combined according to the following rules:

  1. inline-|hidden-|eval - optional value; These three values must appear in the first place. Select one of the three values and cannot be selected repeatedly
  2. nosources - optional value; If there are no above three values, then this value is the first; If so, it's the second one
  3. Soap - optional value; You can follow the module or not
  4. Source map fixed combination, appearing at the end

Taken together, their combination rules are as follows:

[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

Best practices for source map

development environment

Source map or soap module source map is recommended
It can quickly help locate errors and debug code.

  • The Vue scaffold is configured in the development environment as source map, and the production environment is the default value of none
  • The configuration in React scaffold makes a judgment:
devtool:{
	/* Determine whether it is a production environment */
	isEnvProduction?
		/* If so, judge whether to apply source map? */
		shouldUseSourceMap?'source-map':false
		/* If it is not a production environment, judge whether it is a development environment and set it */
	   : isEnvDevelopment && 'cheap-module-source-map'
}

testing environment

Source map or soap module source map is recommended

production environment

It is recommended to use false or the default value, that is, do not write
Avoid the risk of source code leakage

Topics: Webpack