问题描述
从某天起,在大群里有 KOL 用户反馈:在微信客户端里打开商品详情页会出现页面尺寸放大的现象:html

出现的频次不肯定,不能稳定复现,且在北京、长沙几个地域均出现了该状况。前端
技术前提
出现这种现象的前提,是由于前端配置了页面的 viewport meta 标签,并使用 JS 动态根据设备尺寸和像素比来缩放页面,以适配不一样屏幕的手机设备。在下文中,该段技术前提代码统称为『配置』。web
排查步骤
排查的难度在于:没法稳定复现,因此只能不断排查触发条件。chrome
第一种假设:配置失败安全
- 首先想到的是,这段配置并不在全部业务逻辑的最开始,是不是因为这以前的业务逻辑报错致使配置失效?因而将这段配置提高到业务逻辑最前面。失败。
- 再次观察问题描述,发现出现放大的状况,页面并无标题,是不是因为设置标题出了问题(由于在微信里设置页面标题必须用 iframe 的 trick 的方式)?因而去掉了在微信里设置标题的业务代码。失败。
- 经历了两次业务逻辑排查失败,考虑从更高层面分析,是不是业务代码形成的影响?因为页面中大多数核心逻辑的错误都在 Promise 的流程中被 catch 住,并且页面级的 sentry 日志收集方案由于外网不支持,并无保留上报的错误日志,最终可以分析的数据几乎为零。所以搭建了个最精简的页面,只包含配置代码和页面样式。依旧失败。
- 没有了业务代码,是不是配置自己有问题?因而在代码里加上了 try catch,发现并无报错。失败。
这几回失败没有找到问题的缘由,但至少验证了:不是业务代码的错误影响;不是自己配置语法错误的影响。那么,颇有可能这个大的假设『配置失败』是错误的,至少配置的语法没有错误,由于均检测到页面 viewport 的值配置上了。因而开始另外一种假设(实际上也许并无接下来的假设,由于当时准备放弃选择另外一种配置方案,然而巨大的迁移成本让咱们望而怯步,幸亏没有这么作)。微信
第二种假设:配置成功,但没有生效工具
- 没有生效的缘由有不少,是不是语法兼容性的问题?尝试用
content
,setAttribute
,document.write
多种方法配置 viewport。失败。
- 是不是执行时序的问题?设置了页面加载完成后配置、延时配置。失败。
- 是不是微信内置的 webview 的兼容问题?因为问题出现时正好遇上微信客户端的一波升级,强烈怀疑下联系了微信 webview 的开发人员,反复尝试后没法复现。失败。但提出因为之后的微信 webview 使用与 safari 同样的 WK 内核,可使用 safari 尝试复现一下。果真发现 safari 下也有相似的问题偶发,渐渐排除了对微信的怀疑。
- 是不是劫持致使的?在某次不经意发现 chrome 下也有该问题出现,而且发现有非公司域名的 js 脚本注入。因而尝试在测试页面加上外链的劫持脚本。因为安全策略,http 协议的脚本没法在 https 的域名下加载执行,所以看起来也没对配置有影响。失败。
至此,看起来配置是设置成功了,但在某些条件下依旧『没法生效』。在全部条件的排除后线索断了,问题依旧没法稳定复现。不过进一步总结了问题的几个规律:页面被放大、页面无标题、fixed 定位的页面元素失效,都是页面级的影响。并且基本每次有新上线都会出现。测试
一次偶然的状况,再一次在 chrome 下碰到了劫持的状况,因而仔细分析了一下页面,不看不知道:flex

显然,这不是简单的往页面里注入 JS 脚本的劫持,而是整个页面级的劫持,将原有页面包在了 iframe 容器里,外层的页面则是劫持者的页面,而这个页面的 viewport 显然没有通过配置的,因此产生了放大。一样因为设置页面标题的代码在 iframe 内部,只能给 iframe 的窗口设置标题。再者 fixed 的定位限定在了 iframe 内部,天然不会『贴』在窗口上。一切的现象都可以获得解释,那么问题来了:若是真是被劫持了,那么 https 的页面究竟怎样被劫持的呢?调试
第三种假设:页面被劫持
- 现象都能解释的通,但仍是得确认一下。因而在测试页面里判断是否咱们的页面被封装在了 iframe 里,这里只用检测『自己』和『顶层』的页面是不是同一个。果真当出现问题的时候,二者并不是同一个页面,那就存在 iframe 形式的劫持。成功。
- 那么是如何被劫持的呢?是否页面入口是 http 的?不幸的是,页面入口是支持 http 的,只是在 JS 的业务逻辑里会将 http 转成 https 从新请求一遍页面,这是方便可是不安全的,并且目前也有从 http 过来的流量,看起来像掩耳盗铃。但实际的测试页是 https 的,一样有问题的发生,因此即使页面 http 是开放的,也不是此次的缘由。失败。
- 那是不是页面有引用 http 的静态文件致使的呢?由于网上查到有人反馈运营商会劫持并修改 https 页面中引用的 http 的静态文件,从而达到间接劫持的效果。但测试页一样驳回了这种想法,由于页面是最简单的逻辑,全部的代码都是内联而非地址引用的。失败。
- 到底是哪一个环节被劫持了呢?毫无头绪下只能求助 @flex 爸爸了。在梳理了整个请求流程后,发现前端全部的静态文件,包括入口的 html 都上线到了 cdn 上,请求到了 cdn 后,cdn 是用 http 来回源的,多是这段路径受到了劫持。这正好与『基本每次上线都会出现』的现象一致。在配置好回源的 https 协议后,问题总算再也不出现。至此,整个排查也告一段落。
回头看来,这并不是一次目标明确的排查,整个过程在盲目的怀疑中徘徊了好久,其中也有不少因素制约着,好比没有完善的错误日志收集体系、没有 webview 的调试工具,前端的视野限制了咱们的思惟。但也有一些手段起了很大的帮助,好比搭建了一个最精简的测试环境,便于收敛分析;也关注了一些细节,好比详细分析了问题的场景和现象,进而协助目标的定位。
结果是简单的,过程仍是蛮使人回味。仍是那句话:排除全部不可能,剩下的必定是事情的真相。