对于 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 常用于以下几个场景:
本节只讲解如何使用 Source Map,关于 map 文件中字段的含义本节不会解释,有兴趣的读者可以查看参考链接中的文章。接下来我们在 Node.js 环境下以场景 1、3 为例,分别介绍如何将 uglify-es 和 TypeScript 结合 Source Map 使用。
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 实现的,前面讲解过它的用法,这里不再赘述。
全局安装 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)
我们可以在调用 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 文件缓存到内存中,而不是磁盘上。