听说 Hermes 能大幅提升 React Native 的启动速度和运行性能,兴冲冲地开了。结果 App 启动还是慢,列表滚动还是卡。说好的"性能提升"呢?
先搞清楚:Hermes 到底做了什么
Hermes 是 Meta 为 React Native 开发的 JavaScript 引擎,替代了原来的 JSC(JavaScriptCore)。它的核心优化:
| 预编译字节码 | ||
| 增量式 GC |
重点:Hermes 优化的是启动速度和内存占用,不是"让所有东西都变快"。如果你的瓶颈不在启动,开了 Hermes 也不会有明显改善。
App 为什么还是慢
原因 1:瓶颈不在 JS 引擎
Hermes 优化的是 JS 执行速度。但大多数 React Native App 的性能瓶颈在:
Bridge 通信:JS 线程和 UI 线程之间的数据传输 原生渲染:View 的创建、布局、绘制 图片加载:网络图片的下载和解码 动画性能:主线程阻塞
这些问题,换什么 JS 引擎都解决不了。
原因 2:没有用 Hermes 的专属优化
Hermes 支持一些 JSC 不支持的特性,如果你没用上,就白白浪费了:
✅ useTransition / useDeferredValue(React 18+)
Hermes 对 React 18 的并发特性有更好的支持:
import { useTransition, useState } from 'react';function SearchScreen() {const [query, setQuery] = useState('');const [results, setResults] = useState([]);const [isPending, startTransition] = useTransition();const handleSearch = (text: string) => {setQuery(text);// 低优先级更新,不阻塞输入startTransition(() => {setResults(filterResults(text));});};return (<View><TextInputvalue={query}onChangeText={handleSearch} />{isPending ? <ActivityIndicator /> : <ResultListdata={results} />}</View>);}
✅ React.memo + useMemo + useCallback
Hermes 执行这些优化的速度更快,但你得先用上:
import React, { useMemo, useCallback } from 'react';// ✅ 避免不必要的重渲染const ListItem = React.memo(({ item, onPress }: Props) => {return (<TouchableOpacityonPress={() => onPress(item.id)}><Text>{item.title}</Text></TouchableOpacity>);});// ✅ 缓存计算结果function ExpensiveComponent({ data }: { data: number[] }) {const sorted = useMemo(() => [...data].sort((a, b) => a - b), [data]);const total = useMemo(() => sorted.reduce((a, b) => a + b, 0), [sorted]);return <Text>总计: {total}</Text>;}// ✅ 缓存回调函数function Parent() {const handlePress = useCallback((id: string) => {console.log('Pressed:', id);}, []);return <ListItemitem={item}onPress={handlePress} />;}
原因 3:图片没有优化
图片是 React Native App 最大的性能杀手之一。原图不解码、不缓存,直接显示:
// ❌ 没有尺寸限制,直接显示原图<Image source={{ uri: 'https://example.com/photo.jpg' }} />// ✅ 指定尺寸 + 使用 FastImageimport FastImage from 'react-native-fast-image';<FastImagesource={{uri: 'https://example.com/photo.jpg',priority: FastImage.priority.normal,}}style={{ width: 200, height: 200 }}resizeMode={FastImage.resizeMode.cover}/>
原因 4:列表没有虚拟化
渲染 1000 个列表项,即使 Hermes 再快也扛不住:
// ❌ 一次性渲染所有项<ScrollView>{items.map(item => <Itemkey={item.id}data={item} />)}</ScrollView>// ✅ 用 FlatList,只渲染可见区域<FlatListdata={items}renderItem={({ item }) => <Itemdata={item} />}keyExtractor={(item) => item.id}windowSize={5} // 可视区域外保留的屏幕数maxToRenderPerBatch={10} // 每批渲染的数量removeClippedSubviews={true} // 移除屏幕外的视图/>
原因 5:Hermes 的 GC 没有被正确配置
Hermes 的增量式 GC 在某些场景下可能不如预期:
// 在 android/gradle.properties 中hermesEnabled=true// 如果遇到内存问题,可以调整 GC// 在 app.json (Expo) 或原生配置中{"expo": {"jsEngine": "hermes","android": {"jsEngine": "hermes"},"ios": {"jsEngine": "hermes"}}}
真正的性能优化清单
第一层:基础(必须做)
确认 Hermes 已开启 生产包开启了 ProGuard(Android) 图片指定了尺寸,用了 FastImage 或 expo-image 列表用 FlatList,不用 ScrollView + map 移除了 console.log(生产包)
第二层:进阶(推荐做)
用 React.memo包裹列表项组件用 useMemo缓存计算结果用 useCallback缓存回调函数图片用 WebP 格式(比 PNG 小 30%) 启用 RAM bundles(按需加载模块)
第三层:高阶(性能敏感场景)
用 Reanimated 做动画(UI 线程执行) 用 InteractionManager延迟非关键任务用 requestIdleCallback做低优先级工作拆分大 Bundle,用 Code Splitting 用 Flipper 的 Performance 面板找瓶颈
总结
Hermes 是地基,不是装修。地基打好了,房子还是要自己盖。
夜雨聆风