preface

Why use Source Map in Webpack? What are the ways in which Source Maps can be used in Webpack, and how should we use Source Maps in development and production environments

The hwWebPack version used in this article is hwWebPack 1. By convention, you can click to see the Demo Github address

What’s wrong with Webpack packaged code?

We know that Webpack builds a dependency tree and generates the corresponding result file through reference relationships between modules. But the resulting file is flawed

  • Code can be compressed and confused
  • Code files may consist of one or more

These two problems lead to: if your code is reporting an error, how do you locate the problem?

For example, IN my entry file:

console.log('Interesting!!! ')
// Create heading node
const heading = document.createElement('h1')
heading.textContent = 'Interesting! '
console.log(a); // This line will report an error
// Append heading node to the DOM
const app = document.querySelector('#root')
app.append(heading)
Copy the code

Click on Source and you may be confused because the code is compressed and obfuscated and you don’t know what went wrong. This is why we need the Source Map

What is a Source Map

Source Map, as the name suggests, is a file that holds Source code mappings. As mentioned above, we can’t find the information about the file that reported the error. Is there a file that has the mapping between the source file and the packaged file and let it tell us? This file is the Source Map file

How to use Source Map

Suppose we have the Source Map file, how do we use it? We just need to add at the end of the packaged file:

//# sourceMappingURL=main.bundle.js.map
Copy the code

SourceMappingURL Points to the URL of the Source Map file

Source Map file parsing

The Source Map file looks like this:

{
  "version": 3."sources": [
    "webpack://webpack5-template/./src/index.js"]."names": []."mappings": ";;;;; AAAA; AACA; AACA; AACA; AACA,eAAe; AACf; AACA; AACA,mB"."file": "main.bundle.js"."sourcesContent": [
    "console.log('Interesting!!! ')\n// Create heading node\nconst heading = document.createElement('h1')\nheading.textContent = 'Interesting! '\nconsole.log(a); N // Heading node to the DOM\nconst app = document.querySelector('#root')\napp.append(heading)"]."sourceRoot": ""
}
Copy the code
  • Version: Indicates the Source map version. The current value is 3.
  • Sources: indicates the file before conversion. The item is an array indicating that there may be multiple file merges.
  • Names: Names of all variables and attributes before conversion.
  • Mappings: STRING that records location information. This word, used VLQ coding related, detailed can see Ruan Yifeng teacher JavaScript Source Map details
  • File: indicates the converted file name.
  • SourceRoot: Directory of the file before conversion. This item is empty if it is in the same directory as the file before the conversion.

Webpack Source in the Map

Now that we know some of the basic concepts of Source Map, let’s see how Source Map is used in Webpack

Let’s start by looking at the DevTool configuration in Webpack

The official document lists many kinds of combinations, before this, we can take a good look at the following keywords, no matter what combination is one or more of the following combination

  • The source map. Generates.map files (source map files are not generated when used with eval or inline, depending on which mode)
  • The eval. Use Eval to wrap block code
  • Being. No column information is generated
  • The inline. Embed.map as a DataURI instead of generating a separate.map file
  • The module. The source map contains the loader

Next, let’s use some examples to explain

devtool: ‘source-map’

//# sourceMappingURL=main.bundle.js.map, which tells the browser where the source code is located

/ * * * * * * / (() = > { // webpackBootstrap
var __webpack_exports__ = {};
console.log('Interesting!!! ')
// Create heading node
const heading = document.createElement('h1')
heading.textContent = 'Interesting! '
console.log(a); // This line will report an error
// Append heading node to the DOM
const app = document.querySelector('#root')
app.append(heading)
/ * * * * * * / })()
;
//# sourceMappingURL=main.bundle.js.map
Copy the code

The main. Bundle.js. map directory contains more detailed Source map information

{
  "version": 3."sources": [
    "webpack://webpack5-template/./src/index.js"]."names": []."mappings": ";;;;; AAAA; AACA; AACA; AACA; AACA,eAAe; AACf; AACA; AACA,mB"."file": "main.bundle.js"."sourcesContent": [
    "console.log('Interesting!!! ')\n// Create heading node\nconst heading = document.createElement('h1')\nheading.textContent = 'Interesting! '\nconsole.log(a); N // Heading node to the DOM\nconst app = document.querySelector('#root')\napp.append(heading)"]."sourceRoot": ""
}
Copy the code

After packaging, you can look at our page and see the number of rows and columns that reported an error (you can locate a specific column).

devtool: ‘cheap-source-map’

After yarn Build is packaged, we find that the mapping part is different. Mainly because cheap does not generate column information, it is less. We’re testing a small amount of code, so it doesn’t seem to make much of a difference, but when you have a lot of code, it actually does. Specific performance, with the above a little similar, is the point into the details of the time, the cursor will not automatically jump to a specific column. In fact, when we develop a specific line is not just needed, after all, when you locate a line, you can basically determine the problem

{ "version": 3, "file": "main.bundle.js", "sources": [ "webpack://webpack5-template/./src/index.js" ], "sourcesContent": [ "console.log('Interesting!!!')\n// Create heading node\nconst heading = Document.createelement ('h1')\nheading. TextContent = 'Interesting!'\nconsole.log(a); // This line will report an error \n// Append heading node to  the DOM\nconst app = document.querySelector('#root')\napp.append(heading)" ],-+ "mappings": ";;;;; AAAA; AACA; AACA; AACA; AACA; AACA; AACA; AACA;; A",
  "sourceRoot": ""
}

Copy the code

devtool: ‘cheap-module-source-map’

Generate a SourceMaps file with no column information (column-mappings), and the Sourcemap of the Loader has been simplified to contain only the corresponding rows.

{ "version": 3, "file": "main.bundle.js", "sources": [ "webpack://webpack5-template/./src/index.js" ], "sourcesContent": [ "console.log('Interesting!!!')\n// Create heading node\nconst heading = Document.createelement ('h1')\nheading. TextContent = 'Interesting!'\nconsole.log(a); // This line will report an error \n// Append heading node to  the DOM\nconst app = document.querySelector('#root')\napp.append(heading)" ],-+ "mappings": ";;;;; AAAA; AACA; AACA; AACA; AACA; AACA; AACA; AAAA; AACA;; A",
  "sourceRoot": ""
}
Copy the code

devtool: ‘eval-source-map’

The package is just main.bundle.js. Eval-source-map – Each module is executed using eval(), and the source map is converted to DataUrl and added to eval(). The demo will not be repeated

The general code is as follows:

(() = > { // webpackBootstrap
	var __webpack_modules__ = ({
/ * * * / "./src/index.js":
/ * * * / (() = > {
// Keep an eye on this line
eval("console.log('Interesting!!! '); // Create heading node\n\nvar heading = document.createElement('h1'); \nheading.textContent = 'Interesting! '; \nconsole.log(a); Append heading node to the DOM\n\nvar app = document.querySelector('#root'); \napp.append(heading); //# sourceURL=[module]\n//# sourceMappingURL=data:application/json; charset=utf-8; base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly93ZWJwYWNrNS10ZW1wbGF0ZS8uL3NyYy9pbmRleC5qcz9iNjM1Il0sIm5hbWVzIjpbI mNvbnNvbGUiLCJsb2ciLCJoZWFkaW5nIiwiZG9jdW1lbnQiLCJjcmVhdGVFbGVtZW50IiwidGV4dENvbnRlbnQiLCJhIiwiYXBwIiwicXVlcnlTZWxlY3Rvc iIsImFwcGVuZCJdLCJtYXBwaW5ncyI6IkFBQUFBLE9BQU8sQ0FBQ0MsR0FBUixDQUFZLGdCQUFaLEUsQ0FDQTs7QUFDQSxJQUFNQyxPQUFPLEdBQUdDLFFBQ VEsQ0FBQ0MsYUFBVCxDQUF1QixJQUF2QixDQUFoQjtBQUNBRixPQUFPLENBQUNHLFdBQVIsR0FBc0IsY0FBdEI7QUFDQUwsT0FBTyxDQUFDQyxHQUFSLENBQ VlLLENBQVosRSxDQUFnQjtBQUNoQjs7QUFDQSxJQUFNQyxHQUFHLEdBQUdKLFFBQVEsQ0FBQ0ssYUFBVCxDQUF1QixPQUF2QixDQUFaO0FBQ0FELEdBQUcsQ 0FBQ0UsTUFBSixDQUFXUCxPQUFYIiwic291cmNlc0NvbnRlbnQiOlsiY29uc29sZS5sb2coJ0ludGVyZXN0aW5nISEhJylcbi8vIENyZWF0ZSBoZWFkaW5nI G5vZGVcbmNvbnN0IGhlYWRpbmcgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdoMScpXG5oZWFkaW5nLnRleHRDb250ZW50ID0gJ0ludGVyZXN0aW5nISdcb mNvbnNvbGUubG9nKGEpOyAvLyDov5nkuIDooYzkvJrmiqXplJlcbi8vIEFwcGVuZCBoZWFkaW5nIG5vZGUgdG8gdGhlIERPTVxuY29uc3QgYXBwID0gZG9jd W1lbnQucXVlcnlTZWxlY3RvcignI3Jvb3QnKVxuYXBwLmFwcGVuZChoZWFkaW5nKSJdLCJmaWxlIjoiLi9zcmMvaW5kZXguanMuanMiLCJzb3VyY2VSb290I joiIn0=\n//# sourceURL=webpack-internal:///./src/index.js\n");
/ * * * /})});var __webpack_exports__ = {};
	__webpack_modules__["./src/index.js"] (); }) ();Copy the code

devtool: ‘inline-source-map’

We see that we’re actually just packaging the main.bundle.js file without the source map, and that the Souce map is actually embedded in our main.bundle.js file (note the line in diff).

/******/ (() => { // webpackBootstrap var __webpack_exports__ = {}; / *! * * * * * * * * * * * * * * * * * * * * * *! * \! *** ./src/index.js ***! \**********************/ console.log('Interesting!!! ') // Create heading node const heading = document.createElement('h1') heading.textContent = 'Interesting! ' console.log(a); // Heading node to the DOM const app = document.querySelector('#root') app.append(heading) /******/ }) ();+ //# sourceMappingURL=data:application/json; charset=utf-8; base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly93ZWJwYWNrNS10ZW1wbGF0ZS8uL3NyYy9pbmRleC5qcyJdLCJuYW1lcyI6W10sIm1hc HBpbmdzIjoiOzs7OztBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsZUFBZTtBQUNmO0FBQ0E7QUFDQSxtQiIsImZpbGUiOiJtYWluLmJ1bmRsZS5qcyIsInNvd XJjZXNDb250ZW50IjpbImNvbnNvbGUubG9nKCdJbnRlcmVzdGluZyEhIScpXG4vLyBDcmVhdGUgaGVhZGluZyBub2RlXG5jb25zdCBoZWFkaW5nID0gZG9jd W1lbnQuY3JlYXRlRWxlbWVudCgnaDEnKVxuaGVhZGluZy50ZXh0Q29udGVudCA9ICdJbnRlcmVzdGluZyEnXG5jb25zb2xlLmxvZyhhKTsgLy8g6L+Z5LiA6 KGM5Lya5oql6ZSZXG4vLyBBcHBlbmQgaGVhZGluZyBub2RlIHRvIHRoZSBET01cbmNvbnN0IGFwcCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJyNyb290J ylcbmFwcC5hcHBlbmQoaGVhZGluZykiXSwic291cmNlUm9vdCI6IiJ9
Copy the code

summary

The above is just an analysis of some common patterns. Official documentation provides many patterns

But basically they are consistent with the following conclusions

  • The source map. Generates.map files (source map files are not generated when used with eval or inline, depending on which mode)
  • The eval. Use Eval to wrap block code
  • Being. No column information is generated
  • The inline. Embed.map as a DataURI instead of generating a separate.map file
  • The module. The source map contains the loader

Development environment and production environment

What patterns should we use in our development and production environments?

The development environment

For development environments, it is common to want faster Source maps that need to be added to the bundle at the cost of increasing volume. However, in a production environment, you want a more precise source map, which needs to be separated from the bundle and exist independently.

For development environments, eval, eval-source-map, eval-cheap-source-map, eval-cheap-module-source-map, and so on are all available. My personal recommendation is eval-cheap-module-source-map

  • evalThe execution efficiency is high
  • This is a “cheap “source map because it does not generate a column mapping, just the number of rows
  • The source map from the Loader gets better results

The production environment

For production environments, select (None) (omits devtool option) – do not generate the Source map. This is a good choice.

In a special case, you need to use the source map in production — the monitoring system analyzes specific error messages. In this case, you usually choose source-Map — the entire Source map is generated as a single file (of course, if you don’t need to fetch column information, Use cheap-module-source-map instead. It adds a reference comment to the bundle so that development tools know where to find it. However, configure your server to not allow ordinary users to access the Source map file! You should not deploy the Source Map file to the Web server. Instead, use it only for error reporting tools.

The monitoring system analyzes the implementation of specific error messages

I’m going to summarize how it works, and I’m not going to expand it

  • When webPack is built, upload the original JS and Source map files to our monitoring platform
  • Js error stack collection, window.onerror to catch JS errors, and then reported to the server, used to collect user use of the bug
  • Resolves JS errors and maps the source file stack
  • Sourcemap is used to find the original error message, using source-map
  • Display of monitoring platform

conclusion

Because Webpack packaging will confuse and compress the code, we need Source Map to parse the Source file for us, so that we can locate and check the problem. Webpack provides a variety of configurations for DevTool, but we need to understand the general details of Source Map, eval, cheap, inline, and module so that we can learn from each other. We need to adopt different Source Map strategies for production and development environments, with development environments focusing on development efficiency and production environments focusing on performance and security.

Demo Github address, I hope to help you, welcome you to click on the comment collection

reference

  • What are the 7 SourceMap modes in devtool?
  • Break the casserole: Sourcemap in Webpack