桔妹导读:自从 Google 的 Flutter 发布之后,Facebook 对 React-Native 的迭代开始快了起来,优化 React-Native 的性能表现,避免被 Flutter 比下去。最近一个比较大的动作是开源了一个 JavaScript 引擎,并将其包含到 React-Native 中。那么这款引擎它有什么不同,相比 V8、JSC 这些 JavaScript 引擎又有什么优势呢,现在本文来为你揭晓。
1.Hermes 引擎是什么,优势有哪些?
重要的事情提前说:Hermes 引擎是 Facebook 研发,在 React-Native Android 端用于替换 JavaScript Core 的 JavaScript 引擎。Hermes 引擎的优势是适合移动端的轻量级 JavaScript 引擎,使用 aot 编译,可以减少 Android 端内存使用,减小安装包大小,提升执行效率。
2.什么是 JavaScript 引擎?
JavaScript 引擎是一个专门处理 JavaScript 脚本的虚拟机,一般会附带在网页浏览器之中。
3.主流 JavaScript 引擎
V8(Google)、JavaScriptCore(Apple)、SpiderMonkey(Firefox)
4.RN 中的 JavaScript 引擎
Weex,Android:V8,iOS:JavaScriptCore
RN,Android:JavaScriptCore(Hermes、V8),iOS:JavaScriptCore(Apple 要求)
注:Hermes Engine在React-native 0.60.2 版本后支持
5.Hermes 的特色
6.优化原理
截取自code.fb.com传统 JavaScript 引擎通常是以上图的模式完成代码执行的,编译阶段只完成 babel 转义和 minify 压缩,产物还是 JavaScript 脚本,解释与执行的任务都需要在运行时完成(如 V8 引擎,还会在运行时将 JavaScript 编译为本地机器码)很明显缺点就是在运行时需要边解释边执行,甚至需要占用系统资源执行编译任务。Hermes 引擎使用了 aot 编译的方式,将解释和编译过程前置到编译阶段,运行时只完成机器码的执行,大大提高了运行效率。7.已有项目接入 Hermes
升级 React-Native 及相关库升级(成本较小)
因为 React-Native 0.60.x 变更为依赖 AndroidX,所以 Android 项目需要使用 28 以上版本编译,适配 Android 高版本,且需要迁移到 AndroidX(成本较大)
修改 build.gradle,添加 Hermes 相关属性及依赖(成本较小)
8.是否支持 CodePush?
Hermes 引擎预编译后的产物与RN原方式相同,都是在 assets 文件夹下生成的 index.android.bundle 文件。RN 原方式中 index.android.bundle 是经过压缩的 JavaScript 脚本文件,Hermes 预编译后则是二进制文件。因为只有产物文件格式的区别,并没有修改原有JS Bundle 的加载方式,所以 CodePush 可以继续使用。目前 code-push 的两种发布模式支持情况:发布方式 | 是否支持 | 备注 |
| | 需依赖react-native bundle命令完成脚本打包,该命令尚不支持预编译 |
| |
|
9.调试效率
Debug 模式下 Hermes 不开启预编译以支持 Hot Reload ,缺点是 Release 模式下所有Hermes 引擎优势都不存在,甚至因为无 JIT 导致性能还要差于原有引擎。但开发者模式并不追求性能,而更追求调试效率。Debug 模式内置 libhermes-inspector.so ,支持 Chrome inspect 的使用,支持 DevTools 协议,比原有 RN 调试体验更佳(应用内代理,不能同步调试原生调用)10.ES 标准支持
Hermes 支持 ES6,紧跟最新的 JavaScript 规范。为了优化引擎大小,不支持 RN 程序中使用较少的语言特性,如本地 eval()。11.性能调研
▍包大小分析
包大小减小 2MB,整体减少 2MB / 20MB = 10%分析具体包大小减小的原因可以发现,包内容两者只有 lib 大小和 assets 的大小存在差异。对比 lib 内容,发现大小差距主要是由 libjsc.so 和 libhermes.so 两者的差距导致的,即 Hermes 引擎的大小。对比 assets 内容,发现大小变化主要由 index.android.bundle ,即 JavaScript 打包产物引起,Hermes 模式下反而更大的原因是进一步编译为二进制代码。两者影响叠加导致整体减小,包大小得到优化。(支持的平台越多,包体积优化效果越好)▍内存分析
实验方法:在相同的业务页面稳定状态下通过 Memory Profiler 查看内存占用情况内存占用平均减小20MB以上,整体减小20MB / 210MB = 10%分析 Profiler 数据可以发现,内存优化主要发生在 Code 内存区。Google 官方文档中对内存 Code 区的描述:Code:您的应用用于处理代码和资源(如 dex 字节码、已优化或已编译的 dex 码、.so 库和字体)的内存。联系到上个章节中包大小分析中 libhermes.so 尺寸的减小,可以很容易想到,内存占用的减少就是因为 .so 对内存占用的减小。另外两者对 JavaScript 内存的占用也有细微差别,但是可以忽略不计。▍TTI性能
TTI:Time to Interactive,用户可交互时间,启动到页面渲染完成并且可以正常响应用户的输入的时间,衡量用户体验的移动端指标。React-Native Android 中主要是 Application onCreate 开始到 RN 组件渲染完成可交互的时间。
值得吐槽的是,在 iOS 版本的 Pref Monitor 中直接就包含了这个指标的显示,但是 Android 版本的 Pref Monitor 只有四个指标,且并没有 TTI 这一指标。在 Android 平台上可以通过 RN 提供的 ReactFindViewUtil 类获取 RN 组件对应的原生组件,注册对应的渲染回调,在控件渲染完成时记录TTI结束时间。TTI 减少 135ms,整体减少 135ms / 829ms = 16%12.总结
面对 Flutter 的咄咄攻势,React-Native 终于做出了一些改变,Hermes 作为一款适合移动端的 JavaScript 引擎,确实有其性能优势,希望通过本文能够让你更加了解 Hermes。本文首发自普惠出行产品技术 (ID:pzcxtech)滴滴 | 高级软件开发工程师 哲学程序员,是个气管炎、伪学霸,主要研究移动端技术,目前负责终端技术的落地。相信“技术能够改变世界”,喜欢学习新技术