近日,腾讯开源跨端框架 Hippy,一周即吸引3000+star。在腾讯内部,Hippy 已运行3年之久,跨 BG 共有 18 款线上业务正在使用 Hippy,日均 PV 过亿,且已建立一套完整生态。相较于其他跨端框架,Hippy 对前端开发者更友好:紧贴 W3C 标准,遵从网页开发各项规则,使用 JavaScript 为开发语言,同时支持 React 和 Vue 两种前端主流框架。本文为大家介绍了Hippy 常用调试方法和常见问题案例,希望能够帮助开发者快速上手。
前端调试在官网已经有专门章节进行描述,就不多说,这里具体说一下调试常见问题、案例和一些基本原理。
Hippy 已经在 hippy-debug-server 中集成了一套基于 Chrome DevTools Protocol 的调试服务器,启动后在终端进入本地调试界面,便可以进入远程调试模式。
目前 iOS 和 Android 都已经支持了真机调试,Android 通过 adb reverse
命令直接实现了本地调试端口的转发,就是指在手机上访问 localhost:38989
的调试端口时,访问的实际是开发机上的 38989
端口,但是 iOS 需要终端和前端的双方面配合修改端口才可以做到真机调试,所以建议先通过 iOS 模拟器进行调试工作。
启动调试服务、进入终端的本地调试环境后,JavaScript 代码将会通过调试服务加载到真机中运行,如果代码没问题应该能正常运行,但有时候会碰到启动就 Crash 的情况,可以参考常见案例最后一条“iOS 版本低于 9 时模拟器报告 SyntaxError”。
同样的,iOS 上有的特性有的能用 Polyfill 解决,但有的不行(例如 Proxy、正则表达式的 Sticky Flag 等就需要 iOS 10 以上才可以使用,而且无法 Polyfill),所以如果要兼容低版本 iOS,要注意不能使用到太新的 JS 特性。
该方案暂时只适用于 iOS
有的 App 调试模式下运行很正常,但是打完包集成进去以后就挂了,这时候我们需要用到整合后的 jsbundle 包调试大法了。
其实非常简单,Hippy 在 iOS 中时通过自带的 JavaScriptCore 运行的,所以可以通过自带的 Safar 进行调试,在 Safari 的设置 -> 高级打开开发者菜单后 ,启动 Hippy 就能看到多出了一个模拟器设备。
然后就可以用 Safari 开始调试了,唯一要注意的时,断点需要在启动后才生效,启动时是断不下来的,启动问题可以在关键点加上日志,日志能够正常输出。如果是其它启动后问题,可以直接打断点,跟 Chrome 调试服务的使用方法基本一致。
前端开发普遍对内存占用缺乏概念,直到终端同学过来说 JS 内存占用太多把 App 搞崩溃了才回过神来。
JavaScript 目前主要以标记清除算法的方案来进行内存回收,它的核心是定期从全局对象中遍历所有对象,并且对不可到达的对象进行标记,并进而清除。
在绝大多数情况下作为前端开发确实不需要关心内存占用,但是 Hippy 中不太一样,Hippy 是前端的开发方式去开发终端 App,有几个类在组件卸载时一定要记得销毁,包含了 React 中负责事件监听的 EventEmitter 实例、Animation/AnimationSet 动画组件,Vue 中的 $app.on() 终端事件监听等等,不释放掉它们,它们就会一直占用着内存,随着界面越来越多,App 最终将会崩溃。
其实调试方法也非常简单,直接在调试器的 Memory 观察内存占用情况,打快照看一下当时各类对象对内存的占用情况,它是 Hippy 在浏览器里运行的容器,可以代表 App 的整体内存占用情况。
这部分内容 Google 官方也有文档。
这是经常碰到的,最直接的方式是对 React 和 Vue 进行界面绘画的模块 - UIManagerModule 的三个方法 - createNode
、updateNode
、deleteNode
打断点,其实不管 MVVM 怎么做,最终都会通过这三个方法把界面通知终端画上去,这其实也带来了无限的扩展性,任何框架只要对接了这三个方法就可以进行 Hippy 绘制,如果掌握了 UIManagerModule 的语法,甚至不需要 React 或者 Vue 也可以直接通过它画界面。
但从一定程度上来讲,Hippy 画界面的方式其实跟浏览器是不一样的,它是异步的,MVVM 里组件创建完毕,componentDidMount 或者 mounted 后,其实并不意味着界面真的画上去了(但是这个耗时极少,mounted 后基本可以认为真的画上去了),如果要对界面进行操作,需要确定终端确实画上去了才行,这可以通过 onLayout 事件获得;其次可以看到画界面和普通的 Native Module 调用没有本质区别,最终都要通过 JSBridge 进行通讯。-- 这部分正在通过 C++ 方式重写。
通过观察它,我们可以了解到最终通过 React、Vue 解析后的组件是什么样的,可以观察到为什么界面没有更新,或者样式不如预期。
Hippy 的前端框架在开发初期就考虑到了调试的便利性,调试模式下会将前端框架与终端之间的通讯都打印到 Console 里,当觉得自己的业务 App 或者框架显示存在问题时,直接观察它就能很方便获得所有信息。
以 Hippy-Vue 为例:
Hippy-Vue 要关闭该功能只要将入口文件中的 Vue.config.silent 改为 true 即可;Hippy-React 要关闭该功能需要在启动参数里增加一个 silent: true
。不过一般不建议关闭,它在打包后会自动停止输出。
在 Hippy 中只有这两种 View 是可以滚动的,剩下的都不可以滚动,但是要让它们能滚起来也不是那么简单,需要有样式进行配合,简单说就是:
这里的固定高度可以是直接指定高度,也可以是通过 flex 进行界面动态分割的高度,但是一定要是固定的,因为滚动实际是终端去实现的,它需要能够区分可以滚动和不可以滚动的区域,如果容器高度和内容高度一样,那就变成不可以滚动了。
另外 Vue 里的 ul
默认已经加上了 flex: 1 样式会把整个 View 撑满屏幕,一般情况下不用做特别处理,但是 div + overflow-x/y: scroll 依然需要手工指定高度。
当滚动出现异常的时候,可以通过 XCode 调试一下终端代码,它有个 Debug View Hierarchy
功能,可以非常直观地看到界面层级和尺寸,对调试样式问题有很大帮助。
这里需要提到前端三点非常需要注意的地方:
moveNode 功能
,已经计划但仍未完成,指定 key 后在重新排序时会因为对应索引的 key 值不同,先删除全部节点内容,再全部重建,可能会造成轻度闪烁。如果此时不指定 key,就只有一个更新节点的请求,两次请求合并为一次,终端层会对数据进行对比并更新节点内容。经过上面三步,能解决 90% 的 ListView 性能问题。
Hippy-Vue 官方范例中也对这三个参数加了注释。
首先需要检查 numberOfRows 参数是否真的是 ListView 中 ListItemView 的数量,这个除了在业务代码中打断点查看数据数量是否和 numberOfRows 一致以外,也可以通过第一个 UIManagerModule 的调试方法查出来。
这个问题牵扯到 iOS 上一个 ListView 的上屏性能优化,iOS 上并不是发一个 ListItemView 就上屏一个的,而是需要先改变 ListView 的 numberOfRows 再去创建节点,当节点数量与 numberOfRows 一致时再上屏。
目前碰到的所有不渲染的问题都是因为这个原因造成的。
另外在 Hippy-Vue 中,对于静态的 li(就是终端的 ListItemView),可以不需要手工指定 numberOfRows,Hippy-Vue 会在 DOM 层计算子节点数量。但是对于动态获取的数据,也必须要加上该参数,因为 Hippy-Vue 位于 Vue 的渲染层,跟业务还隔了一个 Vue,无法知道业务到底有多少数据准备要渲染。
这里需要提到 Hippy App 的启动方式:当终端 JS 引擎加载完 JavaScript 后,会从 GLOBAL.appRegister 对象里去寻找终端指定的 moduleName,而 __GLOBAL__.appRegister
是在 Hippy 启动时通过 HippyRegister.regist() 方法注册上的,在 Hippy-React 入口文件 或者 Hippy-Vue 入口文件 定义的 appName 最终都会执行到 regist() 方法上进行 __GLOBAL__.appRegister
的注册,首先,我们要检查终端的 moduleName 是否和 appName 一致。
如果一致依然出错的话,很大几率是之前 JS 执行失败,也不排除 SDK 更新后存在 bug,也有可能其它问题,导致 __GLOBAL__.appRegister
未注册成功,但我们有个办法可以在该错误抛出时二次确认一下终端所寻找到 moduleName 是否和前端定义的 appName 一致,可以在那一行打上日志,然后使用上文的 Release 包调试方案检查终端过来查的到底是什么 appName。
这是因为 Hippy 自带的 Webpack 默认调试模式配置文件,最低仅开启了 iOS 9 的输出,因为输出到 iOS 8 会多出很多 polyfill,语法上也会转换,导致体积大很多。
Hippy 本身最低支持的 iOS 8,我们建议在高版本的 iOS 上进行调试,然后打包后在低版本 iOS 走一遍测试流程,没什么问题即可。
如果非要在低版本的 iOS 上进行调试,修改一下 webpack 配置文件 iOS 将 preset-env 中的 ios 版本改成更低即可,但目前经过测试 core-js 对 iOS 8 那样对低版本可能存在问题,这就需要你自己手工调整了。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。