十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

React Native Hermes引擎配置与性能调优实战:从入门到避坑

React Native Hermes引擎配置与性能调优实战:从入门到避坑 说实话我看到oh-my-hermes这个名字的第一反应是这不就是照着oh-my-zsh的命名习惯给Hermes引擎搞的一套配置管理方案嘛。但等我真正把React Native项目里围绕Hermes的配置、踩坑、调优全部过了一遍之后发现事情比想象中复杂得多。Hermes早已不是当初那个可选的实验性引擎而是从React Native 0.70开始成为默认引擎从0.76开始彻底移除了对JSCJavaScriptCore的官方支持。也就是说现在的RN开发者已经不是要不要用Hermes的问题而是怎么把Hermes配置好、用好、调优好的问题。这篇东西我不会写成官方文档的中文翻译也不打算堆一堆概念名词。我按照自己在一个中大型RN项目里实际切换Hermes、调试性能、处理兼容性问题的完整经历来写把我踩过的坑、验证过有效的配置、排查问题的链路以及一些文档里不会告诉你的细节都放进来。如果你是刚接触RN的新手可以把它当一份避开常见坑的实操手册如果你已经在用Hermes但总感觉哪里不太对劲那后面关于字节码缓存、内存治理、灰度回滚的部分应该能帮你解答不少疑惑。1. Hermes引擎凭什么被我列为RN项目的必选项1.1 先理清楚Hermes在React Native里的角色定位很多刚入门RN的开发者会混淆几个概念React Native框架、JavaScript引擎、原生宿主环境。举个例子你就明白了。你写了一个React组件这个组件最终要变成屏幕上能点的按钮、能滚动的列表中间至少经过两层第一层是React的虚拟DOM和协调机制它负责确定界面应该长什么样第二层是原生宿主环境Android的View体系或iOS的UIKit它负责把界面真正画出来。而JavaScript引擎夹在中间所有你写的JS业务逻辑都要先由这个引擎去解析、执行执行结果再通过桥接层或JSIJavaScript Interface传递给原生层。Hermes就是Meta专门为React Native打造的这个JS执行环境。它在2019年发布时主打的就是移动端场景核心设计目标有两个启动够快、内存够省。为了达到这两个目标它做了几个和传统JS引擎很不一样的设计决策。其中最关键的是采用AOTAhead-of-Time预编译机制在应用构建阶段就把JS源码编译成Hermes字节码这样App运行时就不需要再花时间去做源码解析和字节码生成直接把编译好的字节码加载进内存就能执行。同时Hermes还放弃了JITJust-In-Time即时编译。你可能会觉得奇怪V8能跑得那么快不就是靠JIT吗放弃JIT不是开倒车吗这里要结合移动端的特性来看。JIT在运行时会产生额外的编译开销、内存占用而且在iOS平台上由于系统限制JIT能力会受到很大约束。Hermes选择在编译期把能做的优化做完运行时保持轻量。这个取舍在实际App场景里换来的是更稳定的性能和更低的内存峰值。所以现在再看oh-my-hermes这个概念它本质上就是围绕这个引擎的完整配置与调优方案。不是说我写了这个脚本就能让Hermes飞起来而是说通过一套结构化的配置管理方式让你能把这个引擎的能力真正用到位避免那种代码写了一堆、性能却没提上来的尴尬。1.2 我实测过的打开速度与内存占用数据对比说再多理论不如看一组真实数据。我以前维护过一个社区团购类的AppReact Native版本在0.68左右业务比较复杂首页有十几个接口、三十多个组件包体里的JS源码加起来有将近9MB。在切换到Hermes之前App冷启动从点击图标到首页完全可交互在低端Android机上要跑到2.1秒左右iOS大概在1.4秒。而且App在连续切换页面、打开多个图片列表的时候内存会持续上涨低端机上偶尔还会出现卡顿和闪退。后来我把引擎切换到Hermes同一个版本、同一套代码没有做其他任何优化冷启动时间Android降到了1.2秒上下iOS降到了0.9秒左右。内存方面通过Android Profiler和Xcode的Memory Graph观察JS相关的堆内存占用降低了大概20%~30%App整体的内存峰值也有明显下降。这里面最直观的感受是低端Android机上那种打开App先白屏两秒的体验基本消失了用户反馈的闪退也少了很多。这组数据其实非常典型。Hermes的字节码比JS源码更紧凑加载到内存后占用的空间更小而且省去了运行时解析源码的CPU开销所以启动速度和内存表现会同时得到改善。需要说明的是不同项目的提升幅度会不一样。如果你的业务主要是简单列表和静态展示可能感受不明显如果是重交互、多页面、长列表这类吃性能的场景Hermes的收益会非常明显。所以我自己判断一个项目要不要切Hermes一般只看两个条件第一App启动链路里JS执行占的耗时比重高不高第二低端机占比在用户设备里多不多。两个如果都不沾那切不切无所谓只要沾一个Hermes就是必选项。1.3 一个反直觉的事实字节码不等于源码加密这个点我觉得必须单独拎出来讲因为太多人误解了。我在不少技术群里看到过有人讨论Hermes编译成字节码等于给源码加密了别人反编译也看不懂。这话只对了一半。Hermes的.hbc字节码文件确实不像JS源码那样直接可读它里面的字符串、变量名、函数名都已经变成了字节码指令的操作数。但是这离加密还差得远。字节码本质上还是机器可解释的指令序列攻击者完全可以拿到Hermes的反汇编工具把字节码还原成可读性较高的中间表示再通过一些反混淆手段恢复出近似的业务逻辑。我用一个简单的例子来说明。你有一份JS文件里面有一段处理用户优惠券金额的逻辑。编译成Hermes字节码后如果你直接用文本编辑器打开这个.hbc文件看到的确实是一堆乱码但通过对Hermes字节码格式有一定了解的人很快就能定位到字符串区把里面的接口路径、存储键名、错误提示信息这些明文内容一条条抠出来再结合反汇编的指令流还原出大致的业务流程。也就是说字节码提供的是一种增加逆向成本的保护不是绝对安全。所以我的建议是如果你的项目里真的有核心算法、签名逻辑这种不能泄露的东西不要把宝押在Hermes字节码上该上服务端校验就上服务端该做安全加固就做安全加固。Hermes带来的核心价值始终是性能不是安全。把这个预期摆正了后面很多决策就不会走偏。2. 一条命令能不能配好Hermesoh-my-hermes的核心思路2.1 为什么参考oh-my-zsh来做Hermes配置管理熟悉开源生态的人对oh-my-zsh应该都不陌生。它解决的问题是zsh本身虽然强大但配置起来太繁琐插件、主题、别名都要自己一点点弄新环境搭一次要半天。oh-my-zsh把这些东西集中管理起来开箱即用。我在做Hermes配置的时候遇到的情况其实和zsh非常像。单纯在React Native里开启Hermes官方文档给的方法很简单Android侧改一个gradle属性iOS侧在Podfile里改一个参数然后重新构建就行。但真实项目根本不止这么简单。稍微上点规模的项目会有多个build variantdebug、release、staging、不同的渠道包配置、第三方的崩溃监控SDK需要做字节码符号映射、动态更新系统需要知道当前引擎类型、线上问题排查需要能拿到Hermes的堆内存快照……这些需求叠加在一起Hermes的配置就不是开个开关能搞定的了它会散落到gradle配置、Podfile、打包脚本、CI流程、甚至App运行时逻辑里面。如果每次新建项目或者升级RN版本都要把这些配置重新手工撸一遍很容易漏掉某一块而且出了问题非常难排查因为配置分散在不同文件里相互之间还有隐式的依赖关系。oh-my-hermes做的事情就是把这些散落的配置收拢起来做成一个统一的入口。你只需要维护一份配置文件它负责生成各个平台需要的配置片段并在构建流水线里自动注入进去。这样就形成了事实上的单一配置源Single Source of Truth升级引擎版本的时候改的只是一个地方排查问题的时候看的也只是一个地方。2.2 一套配置双端生效的实现逻辑具体怎么做到一套配置双端生效我实际搭过一套方案核心思路是在项目根目录放一个hermes.config.js文件作为所有Hermes相关配置的唯一入口。然后通过Node脚本在构建前读取这个文件动态生成Android侧需要的gradle.properties配置片段和iOS侧需要的Podfile配置代码。这两段生成内容在构建时通过构建工具链注入到各自平台对应的配置环境中去。// hermes.config.js module.exports { enabled: true, memorySize: { android: 256m, ios: 128m }, enableSourceMap: true, enableProguard: false, bytecodeCacheSize: 64MB, debugVariant: { enableHermes: false, enableSourceMap: true } };有人可能会问为什么不直接在gradle.properties和Podfile里写死非要绕一圈生成因为写死的话不同平台之间的配置容易产生漂移。比如某次版本升级你在Android侧改了内存参数但忘记同步到iOS侧双端行为就不一致了。通过一个统一入口生成配置至少能保证你在一份文件里表达的真实意图在双端构建时是一致的。这个一致性的价值在排查为什么Android和iOS表现不一样这类问题的时候会节省大量时间。2.3 配置文件的核心字段与推荐取值既然配置收拢成了一个文件那每个字段到底应该怎么填直接决定后面的效果。我把几个核心字段的取值思路和依据展开讲一下。memorySize这个字段对应的是Hermes运行时JS堆的大小限制。很多团队会忽略这个参数其实它很重要。堆太小遇到复杂页面容易频繁触发GC表现为滚动卡顿堆太大低端机上又会挤占系统内存。我的经验值是Android低端机为主的项目控制在256MB以内中高端设备可以放到384MBiOS上128MB~256MB之间比较稳妥。需要注意的是这只是JS握手时给VM的初始堆预算实际占用还会动态调整。enableSourceMap字段映射文件开启以后才能在线上看到的堆栈里定位到真实的JS源码行号。很多人嫌sourcemap文件大构建慢了就把它关掉等到线上出了错又发现堆栈全是压缩后的单字符变量名根本没法定位。我的建议是release包一定要开而且要通过上传到崩溃监控平台来配合符号还原不能只在本地留着。enableProguard这个字段要注意和Hermes本身的关系。Android的Proguard/R8是作用于Java/Kotlin层的代码压缩混淆Hermes字节码的生成是发生在JS这一侧的两者互不干扰但如果你同时在用一些包含原生代码的第三方SDK它们可能会有自己的混淆规则所以配置的时候要确保Proguard规则文件里包含Hermes相关的keep规则。标准模板里一般有这几条-keep class com.facebook.hermes.** { *; } -keep class com.facebook.jni.** { *; }bytecodeCacheSize和增量编译有关如果你在持续集成环境里频繁打测试包会希望Hermes的编译缓存不要每次都全量重建这个值就是控制缓存放多大的64MB在大多数项目里已经够用。说实话oh-my-hermes这个名字本身有一种把复杂事情变简单的暗示但它的核心价值不在于一键开启这个动作而在于把那么多分散的、容易遗漏的决策点收敛到了一个文件里让做配置的开发者不需要把整个React Native的构建链路都背下来也能做出相对合理的决策。3. 双端配置实操从build.gradle到Podfile的完整链条3.1 Android侧配置gradle属性的作用域与生效时机先看Android侧。React Native项目里的android/gradle.properties文件是gradle读取全局配置的地方。使用Hermes时需要在这个文件里设置hermesEnabledtrue。如果你是RN 0.70之前的版本这个开关默认是false需要手动打开从0.70开始新建项目的默认值已经是true了但老项目升级上来还是得手动确认一下。这里有个很容易踩的坑hermesEnabled这个属性不同React Native版本对它读取的位置不一样。旧版本的react.gradle会在构建React Native bundle这个task时读取它新版本把部分逻辑挪到了app/build.gradle里通过react {}配置块来读取。如果你跨版本升级之后发现配置了hermesEnabledtrue但构建产物里根本没有字节码大概率就是版本升级后配置读取位置变了旧的配置不再生效。遇到这种情况别急着怀疑Hermes先全局搜一下项目里hermesEnabled出现的所有位置确认它被实际引用的位置和当前RN版本的预期一致。另一个容易出问题的点是构建缓存。切换Hermes之后如果只跑增量构建有时会因为旧的构建缓存没有清理导致产物还是老的JSC bundle。我的操作习惯是在切换引擎后一定执行一次cd android ./gradlew clean把build目录整个清掉再做全量构建。这样虽然慢一点但至少能保证这次构建用的配置是全新的。Android侧还可以配置针对不同构建类型的差异化开关。比如你想在debug包上先关掉Hermes、用更方便的调试工具排查问题在release包上再开Hermes可以在app/build.gradle里按build type分别设置react { hermesEnabled project.hasProperty(hermesEnabled) ? project.property(hermesEnabled) : true }3.2 iOS侧配置Podfile变更与pod install的先后关系iOS侧的开关在ios/Podfile里。React Native使用CocoaPods来管理原生依赖Hermes的相关代码库是通过pod来引用的。在Podfile里有一段关于React的配置require_relative ../node_modules/react-native/scripts/react_native_pods use_react_native!( :path config[:reactNativePath], :hermes_enabled true )hermes_enabled这个参数控制的是要不要把Hermes相关的pod依赖引入工程。这里有一个非常烦人的细节Podfile改动之后如果只是执行pod install有时不会重新拉取Hermes相关的依赖因为CocoaPods的缓存策略会认为这个pod没有变化就直接沿用了旧版本的产物。我的做法是在切换Hermes后的第一次构建使用pod install --repo-update必要时还会先把本地的Pods目录删掉再重新install。不要嫌这个操作慢iOS这边链路的正确性比速度更重要。另外RN版本升级的时候react_native_pods.rb这个脚本文件里的默认值可能会变。比如从0.72升到0.74脚本内部对hermes_enabled的默认处理逻辑就调整过。如果升级后没有同步检查Podfile里的配置很容易出现你感知上以为Hermes还开着实际上已经悄悄变成了JSC的情况。我自己被这个问题坑过不止一次后来养成了每次升级RN后都主动去node_modules/react-native/scripts/react_native_pods.rb里搜一下hermes_enabled出现的位置确认当前版本的实际行为。3.3 配置完成后如何验证Hermes确实在跑配置做完怎么确认你的App真的在用Hermes我遇到不少团队改完配置跑起来就以为完事了结果搞了半天线上跑的还是JSC。提供三个我自己常用的验证方法按从简单到复杂的顺序排列。第一个方法最简单App启动后在调试面板或者日志里执行一个表达式typeof HermesInternal ! undefined如果返回true引擎就是Hermes如果返回false说明还在用别的引擎。这个判断的原理是Hermes运行时会在全局挂载一个HermesInternal对象其他引擎不会挂这个属性。第二个方法是看构建产物。RN在release模式下会把JS代码打包成一个bundle文件。如果使用的是Hermes这个文件的扩展名通常是.hbc并且文件头有Hermes字节码特定的魔数标识。你可以去android/app/build/generated/assets/目录下找到release包里的bundle文件用十六进制编辑器打开看一眼文件头Hermes字节码的文件头一般是0xC61F0000开头或者包含HBC字样。第三个方法是在运行时打一个堆内存快照看里面是否有Hermes特有的对象结构。这个方法有点重但你在排查性能问题的时候顺带看一眼就行。如果你想在代码层面做一个校验可以这样if (global.HermesInternal) { console.log(Running on Hermes); }这三个方法配合使用基本能确定App当前实际运行的引擎了。我强烈建议在做完引擎切换之后跑一次这个验证流程不要跳过。因为配置链路长任何一环出错都不会有显式的报错只有验证过了才敢放心往线上推。4. Hermes上线后会踩的坑我都帮你提前试过了4.1 第三方库在Hermes下的兼容性问题排查链路切换到Hermes之后最常遇到的问题就是第三方库的兼容性。这不是Hermes的Bug而是因为JSC和Hermes对ES规范的支持程度、JSI接口的实现方式都存在差异有些第三方库基于JSC的行为写了一些假设或者用到了Hermes不支持的特性就会出问题。我在项目里实际遇到过一个比较典型的案例某个用于图片裁剪的库在JSC下一切正常切到Hermes后在特定机型上偶尔会出现裁剪区域空白的情况。当时团队里的Android开发以为是原生层View的渲染问题查了两天没有结果。后来我顺着JS侧的调用链翻这个库的源码发现它内部有一处对Proxy对象的用法是在defineProperty的getter里返回了一个新的对象。这种写法在JSC下没有问题但Hermes的Proxy实现规范更严格导致返回值在某些场景下丢失了引用。这类兼容性问题排查链路基本是固定的。第一步启用Hermes后先跑一遍全功能回归测试把问题范围圈定在某个模块第二步进崩溃日志或logcat找相关的JS异常堆栈重点关注报错信息里有没有出现Proxy、Reflect、Symbol、Property这类关键词第三步把问题模块的第三方库版本升级到最新看官方是否已经修复了Hermes兼容问题第四步如果升级还没解决才考虑替换这个库。前面我提到的那个裁剪库最终就是通过换了一个底层实现方式不同的替代库才彻底解决的。还有一个经验是排查这类问题的时候不要单看报错信息。Hermes的执行效率高但对ES规范的实现更严格很多在JSC下被容忍的写法在Hermes下会直接抛异常。这类隐性差异很难通过日志直接看出来需要靠对库源码的审查来发现。4.2 Hermes调试工具链的重新适应切换到Hermes后调试工具链和以前JSC时代会很不一样这也是很多开发者刚上手时不适应的地方。在JSC作为RN默认引擎的年代最常用的调试方式是通过Chrome DevTools的chrome://inspect页面来调试JS代码这种方式依赖的是Chrome的DevTools协议和JSC的调试接口。切换到Hermes后你会发现这种调试方式变得不稳定甚至完全不可用。原因很简单Hermes的调试接口走的是CDPChrome DevTools Protocol但在实现细节上和Chrome浏览器里的V8引擎有差异而且React Native原生脚手架对Hermes调试的支持是在React Native Debugger和Flipper这些工具里集成的。我的建议是直接切换到Flipper或者如果新架构下还在过渡期可以同时保留React Native Debugger作为备用。Flipper对Hermes的支持相对完善可以查看网络请求、存储、布局等调试JS代码时的断点、步进、控制台输出都比较稳定。如果你用的是React Native 0.76以上的新架构还可以尝试直接在浏览器里通过React Native团队新出的DevTools工具来调试。调试工具这块我给一个比较现实的建议不要追求工具链的完美哪个能稳定断点、能看控制台就用哪个。开发效率优先级高于一切。4.3 内存缓存清理与开发期困惑这个坑我在实际项目里踩得比较深值得好好说说。现象是这样的某次我把hermesEnabled从false改成true之后重新构建运行发现App跑起来明显变卡而且我改动过的JS代码根本没有生效就像被一个无形的缓存罩住了一样。当时第一反应是Metro的缓存没清于是执行了npx react-native start --reset-cache没用又删掉android/app/build目录重新构建还是没用。后来才定位到问题出在Hermes的字节码缓存机制上。Hermes在release模式下会把编译好的字节码写入设备本地存储下次启动时如果检测到bundle没有变化就直接加载缓存这样能显著提升启动速度。但问题在于这个缓存文件的名字和内容是哈希相关的。如果我的bundle文件没变但Hermes引擎本身的版本变了或者bundle的生成时间戳变了缓存匹配逻辑就会出问题导致加载了陈旧的字节码。这里我给一个可复现的排查手段。当你改了代码但App表现没有变化时不要急着去怀疑业务代码先按顺序检查这几层第一层Metro的transform缓存通过--reset-cache清掉第二层Android的gradle构建缓存./gradlew clean清掉第三层Hermes的运行时字节码缓存在App的存储目录里找到和Hermes缓存相关的目录清理掉。第三层是很多人很容易忽略的因为它的位置比较隐蔽。在Android上路径大概是/data/data/包名/files/hermes/或者/cache/下。开发调试过程中如果你切换过引擎类型强烈建议清一下App的数据否则非常容易出现那种代码改了但没反应的诡异问题。4.4 灰度回滚配置出问题后最稳妥的退路讲了这么多Hermes的好处但我也得诚实地说一句不是所有项目都能平滑切换到Hermes的。有时候某些重度依赖JSC特有行为的库、某些JSI接口实现方式比较特殊的内部SDK就是会在Hermes下出问题而且短时间解决不了。这时候一个成熟的团队不应该在要不要用Hermes上死磕而应该想清楚出了问题怎么退回去。我的做法是在配置阶段就为灰度回滚留好退路。具体说Android侧和iOS侧的Hermes开关都通过构建参数来控制而不是写死在代码里。比如在CI的打包流程里加一个环境变量ENABLE_HERMES构建时把它注入到gradle的hermesEnabled参数和Podfile的hermes_enabled参数中。这样发布的时候可以先让5%的流量走Hermes包如果崩溃率、卡顿率没有异常再逐步放大到全量一旦发现异常直接用构建参数打一版JSC包通过热更新或应用商店发布渠道完成回滚。需要特别强调的是Hermes编译的产物是字节码和普通JS bundle在加载方式上是完全不兼容的所以如果App里还有动态下发代码的机制比如CodePush这类方案你必须确认动态下发的bundle和当前引擎是匹配的。切到Hermes之后动态下发的JS代码同样需要走Hermes的编译流程否则会出现App能启动但动态代码跑不起来的问题。这个点我在项目里遇到过一次就是因为动态下发平台上的bundle还是JSC格式导致部分用户切换引擎后业务模块直接白屏。所以灰度方案一定要把动态内容的下发链路也纳入考虑范围。5. 性能调优从能跑到跑得爽的进阶思路5.1 字节码预编译对启动耗时的真实影响前面的实测数据已经说明打开Hermes后启动速度会有明显提升。但如果你以为只要开了Hermes就等于启动优化做完了那还是想得太简单了。Hermes只是把解析JS源码、生成字节码这段耗时从运行时挪到了构建期真正影响用户感知的启动链路还包括bundle文件的加载和JS业务的初始化执行。在Hermes环境下一个值得做的优化方向是代码拆分。以前JSC时代启动时必须解析整个大的JS bundle即使你只用了其中一小部分功能也必须把全部源码都解析一遍。切到Hermes之后源码解析的开销没了但如果bundle本身还是那个几十MB的大家伙加载速度依然会被IO和网络拖累。这时候可以考虑按业务模块拆分bundle首页需要的核心代码打进主bundle二三级页面和低频功能通过动态加载的方式在需要的时候再拉取。我做过的另一个有效优化是inlineRequire。这个配置的作用是让Metro在打包时把模块间的依赖关系内联到调用处减少模块加载时的查找开销。在Hermes下配合字节码预编译这个优化可以让启动链路的JS执行时间再缩短10%左右。配置方式是在metro.config.js里设置transformer.inlineRequires为true但要注意开启后会增加bundle的体积需要权衡。实际项目中我更推荐按启动环节只加载必要模块的思路来控制这个开关的粒度而不是全量开启。5.2 内存占用治理的常见突破点Hermes本身内存占用比JSC低但如果你业务代码里有内存泄漏点好的引擎也救不了你。切换Hermes后我们花了不少精力做内存治理有几个非常典型的突破点。第一个是全局事件监听器没有移除。React Native的DeviceEventEmitter、AppState.addEventListener这类接口如果在组件销毁的时候没有做清理事件回调会一直持有组件的引用导致整个界面树无法被回收。这类问题在Hermes的GC下虽然也会被发现但内存上升的速度会因为引擎本身的基础占用低而显得更隐蔽需要定期抓堆快照才能定位。第二个是图片缓存。RN的Image组件在加载网络图片时底层通过Fresco或者SDWebImage做缓存如果缓存策略设置得太激进图片占用的内存会远超JS堆表现为App内存整体飙升但你在Hermes的内存指标里根本看不到异常。所以排查内存问题时不要只看JS那一侧原生层的图片缓存、网络缓存都要检查。第三个是定时器泄漏。setInterval如果页面销毁后没有清掉定时器会持续触发持续持有上下文引用。Hermes环境下我建议团队约定在页面组件的useEffect返回函数里统一清理所有定时器并且写一个eslint插件来自动检测漏清的情况。这条约束看着简单但在大型项目里能避免掉大量隐性内存问题。5.3 线下包与线上包的差异检查最后这一点特别容易被忽视但它非常关键。很多开发者在本地开发模式下验证Hermes的性能发现效果并不明显甚至有时候觉得比JSC还慢于是得出Hermes其实没啥用的结论。这个结论是建立在错误前提上的。Hermes的AOT预编译只在release模式下生效。在开发调试模式下为了支持热更新和快速迭代React Native默认不会把JS预编译成字节码Hermes在开发模式下更偏向于解释执行。所以你在debug包上测到的Hermes性能既不代表线上release包的真实表现也不应该成为你是否采用Hermes的决策依据。我踩过的坑是有一次在debug模式下发现Hermes的内存占用比JSC还高差点就把Hermes给回退了。后来我花时间对比了debug和release两种模式下的构建产物才发现在release模式下Hermes的字节码体积比JS源码小将近30%加载速度和执行速度都有明显优势。所以这里给所有人一个建议凡是涉及性能对比的验证必须在release模式下进行最好在真机上跑不要用模拟器。另外如果你在性能测试时发现release包的表现和预期不符优先检查构建脚本里是否误加了dev: true之类的参数这个参数一旦为true即使你打的是release包也会走开发逻辑Hermes的性能优势就体现不出来了。这一条我建议作为发布前的审查清单项每次都过一遍成本极低但能堵掉很多隐形问题。最后分享一个我个人的操作习惯每次给项目做引擎升级或者配置调整后我都会固定跑一轮包含冷启动时间、页面切换帧率、低端机内存峰值、运行半小时后的内存回稳情况这四项指标的基准测试并且把数据记录在项目的性能看板里。这样每一次变更有没有引入性能回退一眼就能看出来。项目稳定性很多时候不是靠运气而是靠这种把关键节点数据化、可对比、可回溯的方式一点一点维护出来的。
返回列表