白屏解决过程
相信很多 hybird 开发程序员,都会遇到或者见过生产线设备白屏的问题。当你准备去解决移动设备白屏问题时,都会感到感到很棘手,像是我们最近就遇到了一个选择器页面,安卓 8.1.0 的用户一打开就直接白屏。为此,我写下基于这个问题,我们的解决过程中遇到的挑战和解决方案。
问题发现
客服反馈,使用的 oppo 安卓 8.1.0 版本的手机用户,无法使用选择器页面,而之前是可以使用的。重新进 app,杀掉进程,甚至重新安装 app 都无法解决问题,希望开发帮忙解决问题
非常经典的手机兼容问题,大部分用户可以使用,唯独某些用户无法使用,那么问题就出在哪呢?
定位问题
好在公司能找到这么古董的安卓手机,测试上手试了发现确实不行,试了其他模块页面发现甚至问题更多了,几个其他代码仓库构建出来的代码都无法使用,一点进去直接就白屏,刚开始以为是原生端没有加载我们的页面,问了原生安卓开发知道并不是的就是页面白屏了。。。
哪能怎么办呢,只能使用 chrome 调试,看下是什么问题了,然后这个时候问题又来了,chrome 调试的时候,必须保证版本不能高于手机上使用的 webview 版本,否则会出现 404,而这个安卓 8.1.0 的安卓手机 app 上使用的 webview 版本是 60 的,现在 chrome 的版本已经更新到 136 版本了,这上哪了去找这个版本的 chrome,公司的网络还是内网根本无法下载。
好在方法总比困难多,好兄弟下载了60 的 chrome 版本,使用自己的电脑安装后,通过 chrome://inspect/#devices,就可以看到手机上运行的 app,神奇的时手机上的 VConsole 并没有输出报错,而在 chrome 浏览器上的控制台有报错,真是是,腾讯你开发团队开源的 VConsole,重写的 console 不行啊~
回到问题上,控制台上报了一个 async/await 的报错 Uncaught ReferenceError: regeneratorRuntime is not defined
解决方案
看来又要搞 babel 了,上babel 原理
因为是 app 的 h5 页面,跟安卓开发扯了会,终于同意开发一个提供我们解决这个问题的 app 版本。于是就开始解决问题
竟然是兼容安卓,首先我想到的是让 babel 转译的代码支持安卓 8.1.0 不就行了吗,于是试了这个配置,而 bebel 的兼容配置是配置在预置的 babel-preset-env 的 target 里于是就有了{ "target": "android": '8.1.0'},不行
换成手机的 webview 60 版本,还是不行
上网找了下 babel 怎么转换 async/await 语法,提到有在 babel plugin 中 babel-plugin-transform-runtime 设置为{"regenerator": true,},不行。
他喵的,只能请大佬看看了,得知结果的我大感离谱,既然是第三方库导致的问题,嘛了,我说我搞了那几次尝试都没有用,原来是引入的库没有兼容低版本。
这个时候就要出多 vue.config.js 构建工具构建第三方依赖了 transplitdependence 配置,打包 node_modules 文件,默认情况下 babel-loader 会忽略所有 node_modules 中的文件。如果你的项目中使用了一些第三方库,这些库可能使用了 ES6+的语法,而这些语法在某些浏览器中可能不被支持,因此需要进行转译。
module.exports = {
// 正则表达式匹配需要转译的依赖
transpileDependencies: ['my-dep', /other-dep/]
}
如果 transpileDependencies 设置为 true 或者一个正则表达式,Vue CLI 会检查 package.json 中的 browserslist 配置,如果项目支持的浏览器范围包括较低版本的浏览器,那么 node_modules 中使用到的高级语法将会被 Babel 编译。如果设置为 false,则 node_modules 中的高级语法将会被原封不动地打包,这可能会导致在低版本浏览器中运行时出现错误。
此外,babel.config.js 的配置也会影响转译过程。例如,以下配置将关闭模块转换,这在编译低版本浏览器并打包后访问时是必要的,以避免找不到 module.exports 的错误:
module.exports = {
presets: [
[
'@babel/preset-env',
{
modules: false,
useBuiltIns: 'usage',
corejs: '3.22'
}
]
],
plugins: []
}
hybrid 应用使用 chrome 调试时,版本为什么需要匹配对应或以下
调试协议: 不同版本的协议差异,如果调试器版本过高或过低,可能导致无法识别的协议指令或数据结构差异。 渲染引擎: JavaScript 引擎 V8 的更新可能改变语法的兼容(如 ES6 支持度),导致调试时语法报错 版本机制: 1.现代 WebView 采用多进程模型(如 Android 8.0 后独立进程),调试时需要建立跨进程通信通道。不同版本的进程间通信机制可能存在差异; 2.WebView 沙箱策略随版本升级不断调整,包括文件访问权限、跨域规则等。调试器需要完全复现生产环境的沙箱配置,否则会出现"本地可调但真机失败"的经典问题
总结: Chrome 调试需匹配 WebView 版本的本质,是调试协议、渲染引擎及安全机制的强版本耦合性。此要求由 Chromium 项目的快速迭代特性决定,尤其在移动端碎片化环境下更为突出。开发者需严格遵循“三位一体”原则:DevTools 协议、Chromedriver、WebView 内核保持大版本一致,方可保障调试链路稳定
edge 为什么不用管调试的 webview 版本
核心在于其基于统一的 Chromium 架构、微软的强兼容性设计以及模块化更新机制,大幅降低了开发者对版本匹配的依赖。
提到 Edge 浏览器内置了 IE 模式,可以解决一些兼容性问题。这可能是 Edge 能够处理不同版本网站的原因之一。 用户可以通过设置让 Edge 自动切换至 IE 模式加载需要兼容的网站。
总结: Edge 通过 Chromium 统一架构消除了内核碎片化,利用 IE 模式和自动兼容性处理覆盖历史遗留问题,加之模块化更新确保环境一致性,使得开发者无需耗费精力在版本匹配上。对于需精确复现旧版环境的极端场景(如企业级 Java 系统),可启用 IE 模式或云测试平台规避
如何实现远程调试
关键是启动了一个 websocket 来做指令中转。
详细可以查看Hybrid 远程调试的前世今生
Android
Android 调试的原理: 本地启动 Unix Domain Socket Server(固定端口 9229)创建 socket client 连上该服务,找到调试页面,发送 cdp 数据包,PC 启动调试工具时,会创建一个 socket client,接着再通过 adb 连上了 Unix Domain Socket Server ,此后即可就进行 cdp 进行通信。
Android 绕过 USB 解决方案其实很简单,加一层 websocket 进行中转。
IOS
原理: OS 和 Mac 之间进行 USB 通信,采用的是 USB 协议称之为 usbmux ,其本身是私有协议,用于自身应用使用,但是都被破解得差不多了...
远程调试 找到远程调试服务的端口。iOS 提供了一种类似「门卫」的解决方案:内部运行一个守护进程(lockdown),运行在固定端口(62078),支持系统服务访问能力。于是可以先通过该服务找到 Web 调试服务(com.apple.webinspector)的端口,之后的过程和 Android 一样了。
总结
启动了一个 websocket 来连接本地协议端口,转发指令; 缺陷: 首先是使用成本,本地需要另外启动一个 socket server 服务 socket server 服务部署在线上呢?网络链路变长了,容易出现时延问题
babel
babel 第三方依赖转换 webpack 通过 js loader 加载 babel
vue.config.js 配置 transpileDependencies
vite 添加@vitejs/plugin-legacy 插件 vite 依赖预构建
legacy({
// 配置需要支持的浏览器范围
targets: ['ie >= 11'],
// 指定polyfill导入来源
additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
}),
babel 和 ts
@babel/preset-typescript