Node.js 调试指南

 主页   资讯   文章   代码   电子书 

4.1.1 什么是 Source Map?

对于 Source Map,想必大家并不陌生,在前端开发中通常要压缩 JavaScript,CSS,以减小体积,加快网页显示。但带来的后果是如果出现错误,就会导致无法定位错误,这时 Source Map 应运而生。举个例子, jQuery 1.9 引入了 Source Map,打开 http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js,最后一行是这样的:

//@ sourceMappingURL=jquery.min.map

这就是 Source Map。它是一个独立的 map(其实就是 JSON) 文件,通常与源码在同一个目录下。

Source Map 常用于以下几个场景:

  1. 压缩代码,减小体积。比如 jQuery 1.9 的源码,压缩前是 252KB,压缩后是 32KB。
  2. 多个文件合并,减少 HTTP 请求数,仅用于前端。
  3. 将其他语言编译成 JavaScript,例如:CoffeeScript、TypeScript 等。

本节只讲解如何使用 Source Map,关于 map 文件中字段的含义本节不会解释,有兴趣的读者可以查看参考链接中的文章。接下来我们在 Node.js 环境下以场景 1、3 为例,分别介绍如何将 uglify-es 和 TypeScript 结合 Source Map 使用。

4.1.2 uglify-es

uglify-js 是最常用的 JavaScript 代码压缩工具,但只支持到 ES5,uglify-es 支持 ES6+ 并且兼容 uglify-js,所以本节使用 uglify-es。

source-map-support 是一个在 Node.js 环境下支持 Source Map 的模块。

安装 uglify-es 和 source-map-support:

$ npm i uglify-es -g
$ npm i source-map-support

创建测试代码:

app.js

require('source-map-support').install()

function sayHello (name) {
  throw new Error('error!!!')
  console.log(`Hello, ${name}`)
}

sayHello('World')

使用 uglify-es 压缩代码文件并生成 map 文件:

$ uglifyjs app.js -o app.min.js --source-map "url=app.min.js.map"

生成 app.min.js 和 app.min.js.map 文件,内容分别如下:

app.min.js

require("source-map-support").install();function sayHello(name){throw new Error("error!!!");console.log(`Hello, ${name}`)}sayHello("World");
//# sourceMappingURL=app.min.js.map

app.min.js.map

{"version":3,"sources":["app.js"],"names":["require","install","sayHello","name","Error","console","log"],"mappings":"AAAAA,QAAQ,sBAAsBC,UAE9B,SAASC,SAAUC,MACjB,MAAM,IAAIC,MAAM,YAChBC,QAAQC,cAAcH,QAGxBD,SAAS"}

此时运行 app.min.js 可以显示正确的错误栈:

$ node app.min.js

/Users/nswbmw/Desktop/test/app.js:4
  throw new Error('error!!!')
        ^
Error: error!!!
    at sayHello (/Users/nswbmw/Desktop/test/app.js:4:9)

如果删除 app.min.js 最后那行注释,重新运行则无法显示正确的错误栈:

$ node app.min.js

/Users/nswbmw/Desktop/test/app.min.js:1
require("source-map-support").install();function sayHello(name){throw new Error("error!!!");console.log(`Hello, ${name}`)}sayHello("World");
                                                                      ^
Error: error!!!
    at sayHello (/Users/nswbmw/Desktop/test/app.min.js:1:71)

source-map-support 是通过 Error.prepareStackTrace 实现的,前面讲解过它的用法,这里不再赘述。

4.1.3 TypeScript

全局安装 TypeScript:

$ npm i typescript -g

创建测试代码:

app_ts.ts

declare function require(name: string)
require('source-map-support').install()

function sayHello (name: string): any {
  throw new Error('error!!!')
}

sayHello('World')

运行:

$ tsc --sourceMap app_ts.ts

生成 app_ts.js 和 app_ts.js.map,运行 app_ts.js 如下:

$ node app_ts.js

/Users/nswbmw/Desktop/test/app_ts.ts:5
  throw new Error('error!!!')
        ^
Error: error!!!
    at sayHello (/Users/nswbmw/Desktop/test/app_ts.ts:5:9)

4.1.4 source-map-support 高级用法

我们可以在调用 install 方法时传入一个 retrieveSourceMap 参数,用来自定义处理 Source Map:

require('source-map-support').install({
  retrieveSourceMap: function(source) {
    if (source === 'compiled.js') {
      return {
        url: 'original.js',
        map: fs.readFileSync('compiled.js.map', 'utf8')
      }
    }
    return null
  }
})

比如将所有 map 文件缓存到内存中,而不是磁盘上。

4.1.5 参考链接

  • http://www.ruanyifeng.com/blog/2013/01/javascript_source_map.html
  • https://yq.aliyun.com/articles/73529
  • https://github.com/v8/v8/wiki/Stack-Trace-API
  • https://github.com/evanw/node-source-map-support

上一节:3.7 uncaughtException + llnode

下一节:4.2 Chrome DevTools