什么是 Sourcemaps
uglifyjs、bable 等工具会对 源代码 进行编译处理生成编译后的代码(下称目标代码),而 sourcemaps 就是保留了目标代码在源代码中的 位置信息
--------- 大神分割线 ---------
如何解读 Sourcemaps
Sourcemaps 是一个 json
{
"version": 3,
"sources": ["a.js", "b.js"], // 源文件列表,这个表示是由 a.js 和 b.js 合并生成
"names": ["myFn", "test"], // 如果开启了变量名混淆,这里会保留变量名在源文件中名字信息
"sourcesContent: [], // 可选项,保存源码信息,顺序与 sources 字段对应,chrome 的 sources 面板中源码使用了这个字段的内容进行展示
"sourceRoot": "", // 源文件所在的目录信息
"file": "dist.js", // 可选,编译后的文件名
"mappings": "" // 这个是重点,是目标代码和源文件的位置的映射关系
}
mappings
目标文件"行"的信息
mappings 是使用 ; 分隔的,每个部分对应目标代码的行
如: “;AAAA;AAAA,BBBB;;”
本例子目标文件有 4 行
第 0 行和第 3 行没有源文件对应信息,所以这两行是编译过程中加入的代码
目标文件的"列"信息
如: “AAAA,CAEA,CAEA;”
‘,’ 表示行内的位置信息分隔符
本例表示目标文件的这一行有三个有效的位置信息。
位置信息的第一位表示目标文件的列的 偏移 信息
本例中,表示列的信息是 ‘A’、‘C’、‘C’,对应的数字为 0、+1、+1,(vlq 编码,在线编解码工具)
注意,这个是偏移信息;
列数从 0 开始,依次累加偏移值可以算出当前的位置信息对应的真正的列
所以本例中表示的是目标文件的第 n 行中的第 0 列,第 1 列,第 2 列(没错是第 2 列)
源文件的信息
如:‘AAAA;ACAA;ADAA;’
位置信息的第二位表示源文件的信息,本例子中是 ‘A’、‘C’、‘D’,对应数字是 0、+1、-1
如果 sourcemaps 中的 sources 字段只有一个文件的话,那么位置信息中第二位一直是 A(不需要偏移)
假设 sourcemaps 中 sources: [‘a.js’, ‘b.js’] 本例的意思是
AAAA: 目标文件第 0 行第 0 列 对应 第 0 个文件 a.js
ACAA; 目标文件第 1 行第 0 列 对应 第 1 个文件 b.js
ADAA; 目标文件第 2 行第 0 列 对了 第 0 个文件 a.js (偏移是 -1 又回到了 a.js)
源文件的行信息
位置信息的第三位表示源文件中的行的信息, 理解了位置偏移的概念,我们很容易理解
如:‘AACA,CACA;AACA;‘
那么
AACA: 目标文件的第 0 行第 0 列 对应 第 0 个文件的第 1 行
CACA: 目标文件的第 0 行第 0+1 列 对应 第 0 个文件的第 1+1 行
AACA:目标文件的第 1 行第 0 列 对应 第 0 个文件的第 1 行 (注意:’;’ 后的行列偏移信息归 0)
源文件中的列信息
位置信息的第四位表示源文件中的列的信息
如:'AAAA,CAAC;'
那么
AAAA: 目标文件的第 0 行第 0 列 对应 第 0 个文件的第 0 行第 0 列
CAAC: 目标文件的第 0 行第 0+1 列 对应 第 0 个文件的第 0 行第 0+1 列
位置信息的第五位
第五位表示变量的偏移,对应 sourcemaps 中的 names 字段,表示目标文件中的变量名对应域源文件中的变量
如:’AAAA,CAACC;AAAAD;'
sourcemaps 中 names 字段是 [‘a’, ‘b’]
那么
AAAA: 目标文件的第 0 行第 0 列 对应 第 0 个文件的第 0 行第 0 列,没有变量的信息
CAACC: 目标文件的第 0 行第 0+1 列 对应 第 0 个文件的第 0 行第 0+1 列,有变量信息,变量在源文件中是 ‘b’ (0+1=1)
AAAAD: 目标文件的第 1 行第 0 列 对应 第 0 个文件的第 0 行第 0 列,有变量信息,变量在源文件中是 ‘a’ (1-1=0)
--------- 大神分割线 ---------
怎么使用 Sourcemaps
Q: 线上小程序报错,我怎么通过 sourcemaps 还原到源代码中?
A: 如报错 appservice.js 1:15000, 表示目标文件第一行 第 15000 列位置报错。根据上文介绍的,通过 mappings 字段算。
Q: 不会。
A: 如果你会写代码的话,参考下边
import fs = require('fs')
import {SourceMapConsumer} from 'source-map'
async function originalPositionFor(line, column) {
const sourceMapFilePath = '如果你不真的替换的成 sourcemaps 在硬盘中的位置,那你还是放弃自己写代码吧。 '
const sourceMapConsumer = await new SourceMapConsumer(JSON.parse(fs.readFileSync(sourceMapFilePath, 'utf8')))
return sourceMapConsumer.originalPositionFor({
line,
column,
})
}
originalPositionFor(出错的行,出错的列)
Q: 不会写代码
A: 下载最新版的开发者工具,菜单-设置-拓展设置-调试器插件
Q: 为啥都是 null?
A: 每个小程序版本都应该对应一个sourcemap文件。 运营中心那里下载的 sourcemap 是对应线上最新的小程序版本。但运营中心的报错集合了多个小程序版本。拿旧小程序版本的报错信息,和最新版本的 sourcemap,是匹配不出的。开发者工具和ci 上传的时候,会提示下载对应版本的 sourcemap 信息,可以自助保存。
Q: 怎么确定有没有版本对应上
A: 下载的 sourcemap 中有个 wx 字段,标明了该 sourcemap 文件对应小程序版本号。
前提
1.确保发生错误的小程序版本和下载回来的 sourcemap 版本是一致的。
a. 下载 sourceMap 文件,可在 mp 后台或开发者工具上传成功弹窗下载
2.确保 map 文件和发生错误的 js 文件是对应的。sourcemap 的目录和文件说明
a. APP 是主包,FULL 是整包(仅在不支持分包的低版本微信中使用),其他目录是分包
b. 每个分包下都有对应的 app-service.js.map 文件。
c. 如果是使用了按需注入特性(app.json中配置了lazyCodeLoading),那么每个分包下还会有 appservice.app.js.map(对应分包下非页面的js),和所有页面的 xxx.js.map
以上事情都确保正确之后,还是出现行列号匹配不出来的情况。那就需要进一步排查。
线上运行的小程序 sourcemap 文件是怎么生成的?
处理流程:源码 [ a.js a.js.map b.js b.js.map ] -> 开发者工具(JS转 ES5,压缩)-> 微信后台(合并 js 文件)[ appservice.app.js appservice.app.js.map]。
注意:如果源码在交给工具之前是经过了 webpack 等打包工具的处理,那源码这里需要有 map 文件。否则不需要存在 map 文件。
可以看出,map 文件经过三个步骤的处理,每个步骤都有可能导致出错,因此开发者需要先排查,是否是前两个步骤出错导致的 map 文件失效的。
如何排查前两个步骤产生的 map 文件是否有问题。
1.排查 a.js.map 文件是否有问题。
a. 可以在 a.js 的代码中写一下 throw new Error(‘test sourcemap’)。
b. 使用了 webpack 的情况下,要构建为生产环境的版本。
c. 在开发者工具模拟器中运行对应的页面,看看控制台中的报错,错误行列号是否能正常映射到源文件。
2.排查 开发者工具(JS转 ES5,压缩)步骤是否有问题。
在排查完第一步的基础上,点击预览,用微信上扫码预览,并打开调试 vConsole 功能,检查 vConsole 中是否有报错信息,检查报错信息中的行列号是否能正常映射到源文件。
如何排查 微信后台(合并 js 文件)是否有问题。
a. 一定要先排查完前两个步骤再来排查这一步,一般情况下,这一步是不会出错的。
b. 如果有问题,也只会导致 map 文件中的行号信息出现偏移。比如 Error 信息中显示报错地址是 100: 200,行号是 100。那么你可能直接用 100: 200 在 map 文件中搜索不出信息,但是如果 用 150: 200 就可以搜索出来,说明行号偏移了 50。那其他报错也可以偏移 50 后再进行搜索就找到结果。
c. 怎么排查偏移了多少?可以结合 error.message 的内容,初步判断大概错误的内容是什么。把对应的 map 文件放到这个网站上 source-map-visualization 进行搜索,找出哪些相同列号的地方。再结合 error.message 的内容进行判断。
d. 如果排查到是这一步导致的问题,请在社区上联系我们,我们会在后续版本进行修复。
依旧排查不出原因?
先整理一下按照上述步骤排查的结论,再在社区上联系我们协助
这个玩意我就一直没定位到过错误,真不知道咋用
看来只能转行了,大家有什么好的行业推荐吗?
版本对应上了,结果还是null。官方就没个人出来真正解决问题!!!
写出一个让人解出全是null的玩意,最后还嘲讽要人转行。特么,该转行的是你们!!!!!!!!!
话是这么说,但是你看看我的问题https://developers.weixin.qq.com/community/develop/doc/000404e52fc15066234b404d351c00?fromCreate=0
下载下来的map中并没有小程序后台的报错文件(下载的是app-service.map.map,报错的是vendor.js)
这就很不符合常理。
你说呢!!
不管是开发工具控制台,自己用source-map,还是浏览器插件 返回都是{ source: null, line: null, column: null, name: null }
是不是文件太大了的原因 我的app-service.map.map也就1.2m
解出全是 null 的不是你们的错。
打开下载的 map 文件,搜索 mappings 看看。所以说下载下来的文件本身就有问题
{ "mappings": ";;;;;;;;;;;" }
{
"mappings": ";;;;;;AAAA,CAEA,CAEA;"
}
未匹配到结果的问题我解决了,确实是因为 sourcemap 和异常版本不匹配,因为下载的 sourcemap 是线上版本,但是异常信息包含历史版本。解决方法是,筛选异常只看线上版本的,这样就都可以匹配上了。
但是这里希望小程序官方直接显示匹配了 source map 以后的原始文件位置,因为微信既有 sourcemap 又有异常信息,其实完全可以帮开发者做好这一步。
跟binnie一起学习~
学习~