×

基本 React Native 性能提示和技巧

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

基本 React Native 性能提示和技巧

在本文中,我们将介绍一套全面的技巧和策略,旨在帮助开发人员优化他们的 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, // 
});

在上面的代码中,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 state 更新本地状态

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

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

相反,直接在 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来渲染项目数组,而不是使用带有 map 函数的 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

打开Android Studio >视图>工具窗口> Profiler。

Profiler 窗口将显示 CPU 和内存使用情况。尝试访问应用中的不同屏幕并观察 Profiler 窗口以确定哪个屏幕占用了较高的 CPU 和内存资源。然后您可以检查代码和日志以修复任何问题。

Xcode

打开Xcode > Xcode 菜单>开发者工具> Instruments。

选择 Instruments 后,将出现一个新的弹出窗口,提供多个选项,例如活动监视器、CPU 分析器、泄漏等。如果您想检查内存泄漏,请单击泄漏选项。在这里,您将找到活动图表以及日志。

2. Chrome 的 DevTools

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

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

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

3. React DevTools

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

React DevTools 提供了对组件重新渲染的洞察,让您可以识别不必要重新渲染的组件并对其进行优化以获得更好的性能。

虽然 React DevTools 没有提供专门针对 React Native 的内置性能分析功能,但您仍然可以将它与其他分析工具(如 Chrome DevTools 或 Xcode Instruments)结合使用来监控渲染时间、CPU 使用率和内存分配等性能指标。

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

4. 翻转器

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

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

5. React Native 性能监视器

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

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

如果想要了解更多,可以访问React Native 官方网站。

删除控制台日志

控制台日志会将日志打印到控制台,从而降低应用性能。因此,建议避免在生产中使用控制台日志。

解决方案 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.logconsole.error_DEV_

使用 babel-plugin-transform-remove-console

babel -plugin-transform-remove-console插件console.log在生产版本中被移除。这将使您的应用程序在生产模式下具有更好的性能。

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

结论

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

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

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

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

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

发表评论: