上周在通过微信公众平台运维中心的错误报告排查问题的时候,发现很多内容提供的报错信息都是编译压缩后的文件代码位置信息,导致很难去根据位置排查到底是哪里出错了,尝试研究了下如何还原源代码进而排查真实错误位置。

定位错误的步骤分为下面三步:

  1. 获取线上环境文件并解包
  2. 获取线上Source Map并定位错误位置
  3. 根据错误位置排查错误

# 一、获取线上环境文件并解包

在微信开发者工具中开发完小程序后,我们点击“上传”按钮,微信开发者工具会进行“编译”,对 JS 代码进行压缩混淆以及对 wxml、wxss 和资源文件等进行整合打包成了 .wxapkg 文件上传给微信服务器。

# 获取小程序包

微信限制了我们直接访问小程序包,我们可以绕过微信直接从微信缓存中拿到我们的小程序包。

工具:一台已越狱的iPhone或已 Root 的 Android 手机。

Android 手机上小程序包的存放目录为:

/data/data/com.tencent.mm/MicroMsg/{UserId}/appbrand/pkg/

iOS 手机上的小程序包的存放目录为:

/var/mobile/Containers/Data/Application/{Wechat Sandbox}/Library/WechatPrivate/{UserId}/WeApp/LocalCache/release/

由于微信的分包策略,我们直接访问小程序拿到的只是小程序的主包,如果想要获取到分包,需要手动访问对应的页面触发微信下载对应的分包

快捷获取所有分包脚本

# 对.wxapkg 进行解包

解包工具和心得

解包后的文件解析:

  • app-service.js: 小程序工程中所有 JS 文件的汇总,已被混淆;
  • app-config.json: 小程序工程 app.json 以及各个页面的 JSON 配置文件汇总,可直接查看;
  • page-frame.html: 所有页面的 .wxml 和 app.wxss 样式文件的汇总,可读性差,需要还原;
  • *.html: 包含每个页面对应的 .wxss 信息,可读性较好;
  • 资源文件: 各类图片、音频等资源文件

# 反编译

这里我们使用wxappUnpacker进行反编译

反编译后,我们就能拿到我们自己线上的微信小程序源码了。

对于有完善版本管理的项目来说,这一步其实很没有意义,因为我们只需要把开发代码切到发布的版本tag就得到了线上代码。

不过在开发者误操作,导致提交给微信的代码并没有上传到git的时候,这一步还是比较有用的。

# 二、获取线上Source Map并定位错误位置

# Source Map 介绍

Javascript越来越复杂,大部分源码都需要经过转换才能投入生产环境。

常见的源码转换主要是以下情况:

1. 压缩,减小体积
2. 多个文件合并,减少HTTP请求数
3. 其他语言编译成JavaScript

通常,JavaScript的解释器会告诉我们,第几行第几列代码出错。但是,这对于转换后的代码毫无用处,使用Webpack打包后一个文件只有几行,每行上万个字符,所有内部变量都改了名字。只看报错信息根本不知道它所对应的原始位置。

这就是Source map想要解决的问题。

SourceMap就是一个信息文件,里面储存着位置信息。也就是说,转换后的代码的每一个位置,所对应的转换前的位置。

# SourceMap的格式

{
  version : 3,
  file: "out.js",
  sourceRoot : "",
  sources: ["foo.js", "bar.js"],
  names: ["src", "maps", "are", "fun"],
  mappings: "AAgBC,SAAQ,CAAEA"
}
  • version:Source map的版本,目前为3。
  • file:转换后的文件名。
  • sourceRoot:转换前的文件所在的目录。如果与转换前的文件在同一目录,该项为空。
  • sources:转换前的文件。该项是一个数组,表示可能存在多个文件合并。
  • names:转换前的所有变量名和属性名。
  • mappings:记录位置信息的字符串,下文详细介绍。

# mappings属性

它保存了两个文件的各个位置是如何一一对应的的信息。

这是一个很长的字符串,它分成三层:

第一层是行对应,以分号(;)表示,每个分号对应转换后源码的一行。所以,第一个分号前的内容,就对应源码的第一行,以此类推。

第二层是位置对应,以逗号(,)表示,每个逗号对应转换后源码的一个位置。所以,第一个逗号前的内容,就对应该行源码的第一个位置,以此类推。

第三层是位置转换,以VLQ编码表示,代表该位置对应的转换前的源码位置。

# 获取小程序的 Source Map

微信官方提供了SourceMap的文件,我们可以通过如下路径去下载小程序的SourceMap文件:

微信公众平台 > 开发 > 运维中心 > 错误查询 > 查询结果 > 下载线上版本Source Map 文件

# 解析 Source Map 为源文件

这一步是可选项,如果我们拿到别人项目的source-map,可以通过这一步来得到源码。

  • 安装 npm 包 reverse-sourcemap
npm i -g reverse-sourcemap
  • 反解map文件
reverse-sourcemap -v filename -o dist

# 通过错误位置定位错误行

我们可以通过 source-map 包提供的功能来定位错误行,如何使用可以查看  Source-map API

懒得看的可以直接使用npm包 source-track,使用方法

npm i source-track -g

sourcetrack ${lineNo} ${columnNo} ${source-map-file.map}

# 根据错误位置排查错误

这里是错误示例:

1580729065941-1e2ac695-cc93-4099-a7c8-970b57d6be8b

通过source-track得到源码错误位置:

1580730339379-9d9e973c-3411-4a50-ba6e-67ecdbbaccd6

根据位置去排查错误即可