source map 是一种能够将处理后的代码映射回源代码的工具,可以帮助我们追踪错误在源代码中的位置
这篇文章,我们将会讲解在webpack中通过 devtool 选项配置使用 source map 的一些事情
1、存在问题
首先我们来看一下,在不使用 source map
的情况下,项目开发会出现什么问题
创建一个空文件夹 Demo
作为项目的根目录,在该目录下使用命令安装项目的所需依赖
> # 创建 package.json > npm init -y > # 安装 webpack > npm install --save-dev webpack > npm install --save-dev webpack-cli
然后我们在根目录下创建 dist
和 src
目录,并在相应的目录下创建相应的文件,最终的目录结构如下
Demo - package.json - package-lock.json - webpack.config.js + node_modules + src - index.js - hello.js - goodbye.js + dist - index.html
webpack.config.js
文件内容,指定 webpack 的一些基本配置
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') } };
/dist/index.html
文件内容,这里只是简单地写了一个 HTML 模板,引入打包之后的 bundle.js
文件
<!doctype html> <html> <head> <title>Demo</title> </head> <body> <script src="bundle.js"></script> </body> </html>
/src/index.js
文件内容,在 index.js
文件中引入 hello.js
文件和 goodbye.js
文件
import { SayHello } from './hello.js' import { SayGoodbye } from './goodbye.js' SayHello() SayGoodbye()
/src/hello.js
文件内容,hello.js
文件导出一个函数,作用是在控制台打印 日志信息
export function SayHello() { console.log('Hello World') }
/src/goodbye.js
文件内容,goodbye.js
文件导出一个函数,作用是在控制台打印 错误信息
export function SayGoodbye() { console.error('Goodbye World') }
然后运行构建命令,以 index.js
为入口构建模块依赖图,将所有依赖的文件打包后输出到 bundle.js
文件
> npx webpack --config webpack.config.js
之后,在浏览器打开 /dist/index.html
文件,在控制台应该可以看到这样的信息
Hello World bundle.js:9 Goodbye World bundle.js:9
由于项目中所有的源代码都被打包成一个 bundle.js
文件
所以堆栈跟踪也只是简单地指向 bundle.js
,无法精确定位到具体哪个文件,这对我们调试没有太大帮助
2、解决问题
那要怎么办呢?好了,这个时候就要轮到 source map 登场啦!
它可以将打包后的代码映射为原始的代码,在发生错误时明确指出究竟是哪个文件发生错误
webpack 通过 devtool
选项指定如何生成 source map,我们修改 webpack.config.js
文件内容如下
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, devtool: 'cheap-module-eval-source-map' };
然后,重新运行构建命令
> npx webpack --config webpack.config.js
打开 /dist/index.html
文件,在控制台应该可以看到打印的信息如下
Hello World hello.js?0278:2 Goodbye World goodbye.js?de2d:2
这时候,打印的日志信息和错误信息终于都指向原始的代码文件啦
3、可选值
事实上, devtool
有很多不同的可选值,选择不同的值会明显影响到堆栈跟踪和构建速度
- 对于开发环境,我们通常希望 更精准 的 source map,这就需要 source map 添加到 bundle 中进去
- 对于生产环境,我们通常希望 更快速 的 source map,这就需要 source map 从 bundle 中分离出来
devtool
选项的可选值如下:
devtool | 构建速度 | 重新构建速度 | 使用环境 |
eval | 非常快 | 非常快 | 开发环境 |
eval-source-map | 慢 | 比较快 | 开发环境 |
cheap-eval-source-map | 比较快 | 快 | 开发环境 |
cheap-module-eval-source-map | 一般 | 快 | 开发环境 |
none | 非常快 | 非常快 | 生产环境 |
source-map | 慢 | 慢 | 生产环境 |
hidden-source-map | 慢 | 慢 | 生产环境 |
nosources-source-map | 慢 | 慢 | 生产环境 |
inline-source-map | 慢 | 慢 | 特定场景 |
cheap-source-map | 比较快 | 一般 | 特定场景 |
inline-cheap-source-map | 比较快 | 一般 | 特定场景 |
cheap-module-source-map | 一般 | 慢 | 特定场景 |
inline-cheap-module-source-map | 一般 | 慢 | 特定场景 |
要记住上面这么多的信息是十分困难的,但是我们不难发现,上面的很多选项其实都是通过几个关键字拼接出来的
- eval:在
bundle.js
文件中,每个模块调用 eval 执行,并且存在 sourceUrl 指向原始文件 - source-map:通过 sourceMappingURL 指向
bundle.map.js
文件,这个文件与原始文件之间存在映射 - eval-source-map:不会生成
map
文件,但是存在 sourceMappingURL 储存map
文件的 Base64 编码 - inline-source-map:不会生成
map
文件,在注释中通过 sourceMappingURL 储存map
文件的 Base64 编码 - cheap:调试的代码不会显示列位置信息
- module:调试的代码是原始的代码,不会经过转换