×

基本的 React Native 性能提示和技巧

作者:Terry2024.03.05来源:Web前端之家浏览:530评论:0
关键词:React Native

最大化 React Native 应用程序的性能对于确保无缝且高效的用户体验至关重要。不幸的是,许多开发人员忽视了性能优化的重要性,通常优先考虑代码的功能,而不考虑其对速度和响应能力的影响。

杰出开发人员的与众不同之处在于他们在编码时认真考虑性能影响的能力。

平衡 JS 线程和主线程之间的动画

在任何应用程序中,动画都是一项艰巨的任务,对于 React Native 来说也是如此。

React Native 使用两个主线程进行操作:用于执行 JavaScript 代码的 JS 线程和主要负责渲染用户界面并响应用户输入的主线程。

默认情况下,动画通常在主线程上运行。但是,主线程上繁重的动画工作负载可能会导致性能问题,例如丢帧。

让我们考虑一个场景,您有一个 React Native 应用程序在屏幕上显示可拖动元素(例如可拖动球)。用户可以使用触摸手势在屏幕上拖动该元素,您的目标是平滑地动画化其移动。

在上面的场景中,当您在屏幕上拖动球时,主线程将忙于收集用户触摸。如果我们在主线程上执行动画,则会进一步增加负担。结果可能会出现丢帧和性能问题。在这种情况下,我们可以使用下面讨论的解决方案将动画任务转移到 JS 线程上。

解决方案 1:尝试使用 useNativeDriver

useNativeDriver是一个 React Native 动画属性,您可以在 React Native 应用程序中对元素进行动画处理时使用。

当用户将此属性的值设置为 true 时,React Native 应用程序将在主线程上渲染动画。但是,如果主线程预计会忙于其他任务,则可以通过设置将动画转移到 JS 线程useNativeDriver: false

例子

Animated.timing(this.state.animatedValue, {
  toValue: 1,
  duration: 500,
  useNativeDriver: false, // <-- Add this to execute animation on the JS thread.
}).start();

在上面的代码中,React Native 将检查该useNativeDriver值并将动画转移到 JS 线程。

解决方案 2:使用 InteractionManager

会出现JS线程和主线程都忙的情况。例如,应用程序可能正在获取 API 数据、执行某些逻辑并将其呈现在 UI 上。

在这种情况下,JS 线程忙于获取数据和执行逻辑,而主线程则忙于显示 UI。当两个线程都参与时,尝试运行动画可能会导致丢帧和性能问题。

在这种情况下,InteractionManager可以利用。您首先启动动画。一旦动画完成,React Native就会调用InteractionManager.runAfterInteractions执行JS代码。然后 JS 代码将调用 API 并在 UI 上显示数据。

这种方法有助于避免同时执行 JS 代码和动画而导致 JS 线程过载。

例子

InteractionManager.runAfterInteractions(() => {
  /* Code to run after Animation completed */
});

避免不必要的重新渲染

避免 React Native 中不必要的重新渲染对于保持最佳性能至关重要。每当应用程序重新渲染时,JS 线程都会创建一个 JS 捆绑文件,并将其通过 React Native 桥传递,然后将其交给主线程。

应用程序重新渲染的次数越多,JS 线程和主线程之间发生的传递就越多,这会降低应用程序的性能。

解决方案 1:记忆组件

React.memo是 React 提供的一个高阶组件,用于通过防止不必要的重新渲染来优化功能组件。

当你用 包装一个功能组件时React.memo,React 会记住该组件,这意味着只有在组件的 props 发生变化时它才会重新渲染该组件。如果渲染之间的 props 保持不变,React 将重用之前渲染的结果,从而避免再次渲染组件的成本。

例子


const MyComponent = React.memo((props) => {
  // component logic
});

解决方案 2:学会明智地使用 useCallback 函数

当父组件为其子组件设置回调函数时,每当父组件重新渲染时,回调函数也会重新创建,从而返回一个新的函数引用。

因此,子组件将此视为回调函数值的更改,提示其重新渲染,即使React.memo已使用。因此,子组件确实会重新渲染。

为了缓解这种情况,请利用useCallback防止在每次重新渲染父组件时重新创建函数引用。


如果您想使用具有新状态值的回调函数,则必须重新创建该函数。要使用更新的状态值重新创建函数,您可以利用 中的依赖项部分useCallback

通过将状态值添加到依赖项数组(如下面的示例代码所示),useCallback将仅在状态值更改时重新创建函数。因此,您将获得新的函数引用和更新的值。

例子

const memoizedCallback = useCallback(() => {
  // callback logic
}, [dependency]);

解决方案3:尽量避免用Redux状态更新本地状态

使用 Redux 数据更新状态可能会导致组件渲染两次:第一次是在更新 Redux 数据时,第二次是在使用 Redux 状态更新本地状态时。

通过遵循这种方法,我们可以避免不必要的组件重新渲染。因此,尽量避免使用 Redux 更新的存储数据来更新本地状态。

相反,直接在 UI 中使用 Redux 存储值。如果在最初显示任何 Redux 数据之前需要进行计算,则仅使用具有 Redux 状态的组件状态。

解决方案 4:记住函数结果

用 记住函数结果useMemo。这将执行该函数并将值存储在内存中。

当应用程序再次调用该函数时,它会从记忆存储中检索数据,而不是重复执行整个函数。您可以添加依赖项以重新执行该函数并存储新结果。

例子

const handleClick = useMemo(() => {
  // handle click
}, [dependency]);

解决方案 5:避免内联函数

避免使用内联函数。相反,请使用箭头函数和useCallbackuseMemo防止不必要的重新渲染。

当父组件重新渲染时,也会使用新引用重新创建函数。如果我们将任何函数作为 prop 传递给子组件,子组件也会重新渲染。

为了解决这个问题,我们可以使用useCallback函数来防止函数重新创建。

此外,通过使用useMemo,我们可以避免重新渲染子组件。

此外,通过使用命名函数而不是内联函数,您的代码将变得更具可读性和可维护性。

例子

<Text onPress={() => pressed()}>Press Me</Text> // Avoid this inline function.
<Text onPress={pressed}>Press Me</Text> // recommended.

上面推荐的示例将直接调用该函数,而不是再创建一个函数。

优化图像

优化图像可以提高应用程序性能。我们可以利用react-native-compressor npm 包来减小上传和查看的图像大小。此外,我们可以使用 SVG 图像来显示图标和其他图像。

解决方案 1:使用 SVG 图标和图像

SVG 文件包含描述图像/图标路径和线条的 XML 代码。SVG 图像与分辨率无关,允许它们缩放到任何尺寸而不会损失清晰度。

由于 SVG 图像是使用矢量路径而不是像素数据渲染的,因此在应用程序中渲染时它们通常消耗更少的内存。这可以提高图像加载性能,尤其是在内存资源有限的设备上。

SVG 文件的文件大小往往比 PNG 或 JPEG 等光栅图像格式更小,尤其是对于简单或几何图形。这会缩短下载时间并减少应用程序的总体内存占用。

解决方案 2:使用 WebP 图像实现无损图像质量

WebP 是 Google 开发的一种现代图像格式,可以在不影响质量的情况下有效减小图像大小。WebP 图像尺寸较小,但保持良好的质量,可以更快地在屏幕上显示。

解决方案 3:缓存图像以加快渲染速度

利用缓存机制存储之前加载的图像,减少重复从网络获取图像的需要。您可以使用react-native-fast-image npm 包缓存 URL 图像。该库将立即在屏幕上显示缓存的图像。

使用稳定的 NPM 包

选择更小、更稳定的 npm 包。这些包不仅减少了 React Native 应用程序的大小,而且由于其稳定的代码库而确保了效率。

为了确定适合您功能的包,请考虑 npm 网站上的以下几点:

  1. 比较具有相同功能的不同类型的 npm 包。

  2. 检查 npm 包的每周下载量。每周下载次数较多的软件包是更好的选择。

  3. 检查 npm 包大小。尺寸较小的包将在项目中占用较少的空间,从而减小项目的大小。

  4. 检查发布数量。版本数量较多表明开发人员正在积极维护。

  5. 检查最后的更新。这有助于确定该软件包是否仍在由某人维护。

遵循这些要点将有助于选择正确的 npm 包。

对组件使用样式表

利用StyleSheet模块来设计组件的优点是仅在初始调用期间创建对象。后续调用将重用已创建的样式对象引用。这种方法有助于减少每次重新渲染时创建样式对象。

使用 Flatlist 提高性能

使用FlatList渲染项目数组,而不是使用 ScrollView 和地图函数。FlatList 仅渲染当前在屏幕上可见的项目,这有助于通过仅渲染必要的项目来优化代码。

避免内存泄漏

内存泄漏是指程序不释放已分配但不再使用的内存 (RAM) 的情况。

随着时间的推移,如果不解决内存泄漏问题,可能会导致程序消耗越来越多的内存,最终导致程序速度变慢,甚至由于可用内存耗尽而崩溃。

我们可以使用 Android Studio 和 iOS Xcode 中的分析工具来检测内存泄漏。这些工具有助于识别内存消耗增加的区域,尽管查明确切原因可能具有挑战性。检测应用程序中的内存泄漏可能很困难,因此最好牢记上述几点以防止内存泄漏问题。

解决方案 1:取消注册计时器/侦听器/订阅

当我们注册任何计时器、侦听器或订阅时,在组件卸载期间取消注册它们非常重要。否则,即使我们不在这些组件上,这些计时器、侦听器或订阅也将继续调用事件,从而导致未使用的内存增加。

例子

useEffect(() => {
  // Registering event listener when component mounts
  const handleAppStateChange = (nextAppState: AppStateStatus) => {
    console.log("App state changed to:", nextAppState);
  };
  AppState.addEventListener("change", handleAppStateChange);
  // Unregistering event listener when the component unmounts
  return () => {
    AppState.removeEventListener("change", handleAppStateChange);
  };
}, []);

解决方案 2:避免使用不必要的全局变量

只要应用程序正在运行,全局变量就会持续存在,因为可以从应用程序范围内的任何位置访问它们。因此,垃圾收集器将它们解释为仍在使用中,并且不会释放它们。

解决方案 3:循环对象引用

在两个对象彼此指向的位置创建对象引用将阻止垃圾收集,因为垃圾收集器假定这些对象仍在使用中。

例子

const person1 = {
  name: "Alice",
  friend: person2,
};

const person2 = {
  name: "Bob",
  friend: person1,
};

React Native 调试和性能监控工具

调试本身主要侧重于识别和解决应用程序代码库中的问题,而不是直接提高性能。然而,通过调试过程,开发人员可以识别并解决与性能相关的问题,例如低效算法、过度重新渲染或内存泄漏,这最终可以提高性能。

调试和性能监控是开发 React Native 应用程序的关键方面,以确保流畅高效的用户体验。下面我会提到一些用于调试和性能监控的工具。

1. 分析工具

使用 Android Studio Profiler(适用于 Android)和 Xcode Instruments(适用于 iOS)等平台提供的分析工具。这些工具可以深入了解 CPU 使用情况、内存分配、网络请求和其他性能指标。您可以按照以下步骤在 Android Studio 和 Xcode 中打开分析器。

注意:识别内存泄漏并不总是那么简单,但这些工具可以帮助分析代码中的潜在问题。

安卓工作室

打开Android Studio >查看>工具窗口>分析器。

Profiler 窗口将显示 CPU 和内存使用情况。尝试访问应用程序中的不同屏幕,并观察分析器窗口以识别哪个屏幕占用较高的 CPU 和内存资源。然后您可以检查代码和日志以解决任何问题。

Xcode

打开Xcode > Xcode 菜单>开发人员工具>仪器。

选择 Instruments 后,将出现一个新的弹出窗口,提供多个选项,例如 Activity Monitor、CPU Profiler、Leaks 等。如果要检查内存泄漏,请单击泄漏选项。在这里您将找到活动图和日志。

2.Chrome的开发工具

Chrome 的 DevTools 是调试 React Native 应用程序的常用工具。虽然主要是为调试 Web 应用程序而设计的,但它的 JavaScript 功能也允许调试 React Native 应用程序。它允许您检查网络活动、内存使用情况和 JavaScript 性能。

要在 Windows 上使用它,请按CTRL+ M,在 macOS 上按Command+ R。如果您使用的是物理设备,则可以摇动移动设备以打开手机上的窗口。

注意:如果您使用的是 React Native 0.73 或更高版本,则无法使用它,因为它已被弃用。

3.React开发工具

React DevTools 允许您检查 React Native 应用程序中各个组件的 props 和状态。通过监控 props 和 state 的变化,您可以识别潜在的性能瓶颈。

React DevTools 提供了对组件重新渲染的深入了解,使您能够识别不必要地重新渲染的组件并对其进行优化以获得更好的性能。

虽然 React DevTools 不提供专为 React Native 定制的内置性能分析,但您仍然可以将其与 Chrome DevTools 或 Xcode Instruments 等其他分析工具结合使用,以监控渲染时间、CPU 使用率和内存分配等性能指标。

您可以运行npx react-devtools命令打开React DevTools。

4. Flipper

Flipper是Facebook开发的一款功能强大的调试工具,支持React Native。它提供了各种用于检查网络请求、数据库查询和 UI 渲染性能的插件。

Flipper 中的布局检查器插件允许您可视化和检查 React Native 组件的层次结构。

5.React Native性能监视器

这是React Native本身提供的内置性能监控工具。它允许您直接在应用程序中监控各种性能指标,例如 FPS(每秒帧数)、CPU 使用率、内存使用率和网络请求。

要在 Windows 上使用它,请按CTRL+ M,在 macOS 上按Command+ R。您将获得“每台显示器显示”选项。

如果想了解更多,可以访问React Native官网:https://reactnative.dev/docs/performance。

删除控制台日志

控制台日志可能会通过将日志打印到控制台来降低应用程序性能。因此,建议避免在生产中使用控制台日志。

解决方案1:使用_DEV_全局变量

在实用程序文件中使用以下代码并在任何地方重复使用它。console.log此代码仅在开发模式下在 的帮助下执行_DEV_,但在生产模式下避免执行。

例子

const logger ={
  log: __DEV__ ? console.log : ()=>{},
  error:__DEV__ ? console.error : ()=>{}
}
logger.log(“show values†,data);

在此代码片段中,logger.log只有当计算结果为 true 时,logger.error才会分别使用console.log和,表明应用程序正在开发模式下运行。否则,它们将被设置为空函数,确保它们在生产模式下不起作用。console.error_DEV_

使用 babel-plugin-transform-remove-console

babel -plugin-transform-remove-console console.log插件在生产版本中删除。这将为您的应用程序在生产模式下带来更好的性能。

React Native 生态系统中有许多可用于调试和性能监控的工具,包括Reactotron、Firebase Performance Monitoring等。详细解释所有这些工具会使文章更长。因此,您可以专注于使用上述任何一种工具来识别和解决问题。

结论

总之,优化 React Native 应用程序性能对于无缝用户体验至关重要。通过平衡动画、最小化重新渲染、优化图像以及使用稳定的 npm 包,开发人员可以提高应用程序速度和响应能力。

此外,利用高效的调试工具并删除不必要的控制台日志可以进一步提高性能。

优先考虑这些优化技术可确保在当今竞争激烈的应用程序市场中实现最佳性能和卓越的用户满意度。

您的支持是我们创作的动力!
温馨提示:本文作者系Terry ,经Web前端之家编辑修改或补充,转载请注明出处和本文链接:
https://jiangweishan.com/article/ReactNative20240305.html

网友评论文明上网理性发言 已有0人参与

发表评论: