React Native性能优化

1. React Native 中如何优化应用的性能?

优化 React Native 应用性能可以提高用户体验并降低资源消耗。以下是一些关键优化技巧:


1. 优化渲染和更新逻辑

  • 避免不必要的渲染

    • 使用 React.memoPureComponent 来避免组件不必要的重新渲染。
    • 合理使用 shouldComponentUpdateReact.useCallback 优化函数组件的性能。
  • FlatList 优化

    • 使用 keyExtractor 提供唯一键值。
    • 通过设置 initialNumToRendermaxToRenderPerBatchwindowSize 控制渲染的批次和范围。
    • 使用 getItemLayout 提供固定高度以提高滚动性能。
  • 减少匿名函数和内联对象

    • 避免在 render 方法中直接定义函数和对象,将它们提取为组件外的常量或使用 useMemouseCallback 缓存。

2. 优化 JavaScript 性能

  • 避免复杂计算

    • 将繁重的计算逻辑放到后台线程,使用库如 react-native-worker 或 Web Worker。
  • 减少 re-renders

    • 利用 Context 或 Redux 管理全局状态,但避免不必要的深度嵌套,结合 useSelectoruseContext 的浅比较。

3. 图片优化

  • 图片格式

    • 使用压缩后的图片(如 JPEGWebP),避免加载超高分辨率图片。
  • 缓存

  • 按需加载

    • 通过惰性加载只加载当前屏幕需要的图片。

4. 网络请求优化

  • 压缩和缓存

    • 启用 GZIP 压缩和 HTTP 缓存。
    • 使用本地数据库(如 Realm 或 SQLite)缓存频繁请求的数据。
  • 批量请求

    • 合并多个请求以减少网络开销。
  • 节流和去抖

    • 对高频操作(如搜索)使用节流(throttle)或去抖(debounce)优化。

5. 减少启动时间

  • 代码拆分

    • 使用按需加载的方式分拆代码,减少主包体积(如 react-native-screens 的懒加载功能)。
  • 启用 Hermes 引擎

    • Hermes 是 Facebook 为 React Native 应用设计的 JavaScript 引擎,可减少应用启动时间并优化内存使用。
  • 禁用 Dev Tools

    • 确保在生产环境中关闭 debugyellow box 和其他开发工具。

6. 动画优化

  • 使用原生驱动动画
    • 使用 Animated 中的 useNativeDriver: true,将动画计算移至原生线程。
  • 选择性能友好的动画库
    • react-native-reanimatedlottie-react-native,能更高效地处理复杂动画。

7. 内存管理

  • 避免内存泄漏

    • 在组件卸载时清理定时器、订阅和事件监听器。
    • 在异步操作中添加 isMounted 检查。
  • 管理大列表

    • 对长列表使用 FlatListSectionList,避免使用 ScrollView 加载所有数据。

8. 监控和调试

  • 使用性能分析工具:
    • React DevTools:分析组件渲染问题。
    • Flipper:用于检查网络请求、日志和性能。
    • Android Studio 和 Xcode Profiler:分析原生性能瓶颈。

2. 如何减少重新渲染?

在 React 或 React Native 中,减少组件的重新渲染是优化性能的关键。以下是一些减少不必要重新渲染的方法:


1. 使用 React.memo

React.memo 可以防止函数组件在相同的 props 下重新渲染。

jsx 复制代码
const MyComponent = React.memo(({ prop1 }) => {
  console.log("Rendering");
  return <Text>{prop1}</Text>;
});

如果 prop1 未改变,组件不会重新渲染。


2. 避免匿名函数和内联对象

匿名函数或内联对象在每次渲染时都会生成新的引用,可能触发子组件的重新渲染。

问题示例

jsx 复制代码
const Parent = () => {
  return <Child onClick={() => console.log("Clicked")} />;
};

解决方案

jsx 复制代码
const handleClick = useCallback(() => {
  console.log("Clicked");
}, []);

const Parent = () => {
  return <Child onClick={handleClick} />;
};

useCallback 确保函数引用在依赖不变时保持稳定。


3. 使用 useMemo 缓存计算值

复杂的计算结果可以通过 useMemo 缓存,避免每次渲染都重新计算。

jsx 复制代码
const Parent = ({ items }) => {
  const processedItems = useMemo(() => {
    return items.map(item => item * 2);
  }, [items]);

  return <Child items={processedItems} />;
};

4. 合理使用 shouldComponentUpdateReact.PureComponent

对于类组件,可以通过 shouldComponentUpdateReact.PureComponent 控制是否需要重新渲染。

jsx 复制代码
class MyComponent extends React.PureComponent {
  render() {
    console.log("Rendering");
    return <Text>{this.props.value}</Text>;
  }
}

5. 使用 key 提高列表性能

在列表渲染中,确保 key 唯一且稳定,避免 React 重新创建 DOM 节点。

jsx 复制代码
<FlatList
  data={data}
  renderItem={({ item }) => <Item key={item.id} {...item} />}
  keyExtractor={(item) => item.id.toString()}
/>

6. 避免不必要的 useEffect 执行

useEffect 中的逻辑如果没有依赖变化,可以跳过执行。

问题示例

jsx 复制代码
useEffect(() => {
  console.log("Effect executed");
});

解决方案

jsx 复制代码
useEffect(() => {
  console.log("Effect executed");
}, []); // 添加依赖项数组

7. 分离状态

将状态切分到更小的组件或 Context,避免一个状态变化影响整个组件树。

问题示例

jsx 复制代码
const Parent = () => {
  const [count, setCount] = useState(0);
  return (
    <>
      <ChildA count={count} />
      <ChildB />
    </>
  );
};

解决方案

使用状态上下文分离:

jsx 复制代码
const CountContext = React.createContext();

const Parent = () => {
  const [count, setCount] = useState(0);
  return (
    <CountContext.Provider value={count}>
      <ChildA />
      <ChildB />
    </CountContext.Provider>
  );
};

8. 使用状态管理工具

如果组件需要共享全局状态,可使用 Redux 或 Zustand 等状态管理工具,避免通过 props 层层传递。


9. 避免过大的组件树

将组件拆分为更小的功能性组件,减小单个组件的渲染范围。


10. 检测和优化性能

使用工具分析性能瓶颈:

  • React DevTools:检查组件渲染的频率。
  • Profiler API:分析组件渲染时间。
jsx 复制代码
import { Profiler } from "react";

const Parent = () => (
  <Profiler id="Parent" onRender={(id, phase, actualTime) => {
    console.log({ id, phase, actualTime });
  }}>
    <Child />
  </Profiler>
);

3. 如何使用 memo 和 useCallback?

在 React 中,React.memouseCallback 是优化组件性能的常用工具。它们可以帮助减少不必要的重新渲染,从而提高应用的效率。


React.memo 的用法

React.memo 是一个高阶组件,用于优化函数组件。它会对组件的 props 进行浅比较,如果 props 没有变化,就跳过重新渲染。

基本用法

jsx 复制代码
import React from "react";

const MyComponent = React.memo(({ value }) => {
  console.log("Rendered!");
  return <div>{value}</div>;
});

const Parent = () => {
  const [count, setCount] = React.useState(0);
  return (
    <>
      <MyComponent value="Hello" />
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </>
  );
};
输出

即使 Parent 组件重新渲染,MyComponent 也不会重新渲染,因为它的 props 没有变化。


与自定义比较函数结合

React.memo 默认使用浅比较,但你可以通过传递自定义比较函数来处理复杂 props

jsx 复制代码
const MyComponent = React.memo(
  ({ value }) => {
    console.log("Rendered!");
    return <div>{value.text}</div>;
  },
  (prevProps, nextProps) => {
    // 自定义比较:如果 `value.text` 没变,不重新渲染
    return prevProps.value.text === nextProps.value.text;
  }
);

useCallback 的用法

useCallback 是一个 React Hook,用于缓存函数的引用。它会在依赖未改变时返回相同的函数实例。

为什么需要 useCallback

在 React 中,每次渲染都会重新创建函数实例。通过 useCallback 可以避免不必要的函数重新创建,从而减少传递给子组件的 props 变化。

基本用法

jsx 复制代码
import React, { useState, useCallback } from "react";

const Child = React.memo(({ onClick }) => {
  console.log("Child Rendered!");
  return <button onClick={onClick}>Click Me</button>;
});

const Parent = () => {
  const [count, setCount] = useState(0);

  // 如果不使用 useCallback,每次渲染都会生成新的函数实例
  const handleClick = useCallback(() => {
    console.log("Button clicked");
  }, []); // 依赖为空,函数实例只会创建一次

  return (
    <>
      <p>Count: {count}</p>
      <Child onClick={handleClick} />
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </>
  );
};
输出
  • 每次 Parent 重新渲染时,由于 useCallbackChild 组件不会重新渲染,因为 onClick 函数引用没有变化。

useCallbackReact.memo 配合

useCallback 常与 React.memo 一起使用,确保传递给子组件的回调函数引用不变。

jsx 复制代码
const Child = React.memo(({ onClick }) => {
  console.log("Child Rendered!");
  return <button onClick={onClick}>Click Me</button>;
});

const Parent = () => {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    console.log("Button clicked");
  }, []);

  return (
    <>
      <Child onClick={handleClick} />
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </>
  );
};

useCallback 的依赖管理

确保正确传递依赖项,否则可能导致回调函数的行为异常:

jsx 复制代码
const Parent = () => {
  const [count, setCount] = useState(0);

  // 如果依赖项未设置,当 count 变化时,函数不会更新
  const handleClick = useCallback(() => {
    console.log(count);
  }, [count]); // 添加 count 作为依赖

  return <button onClick={handleClick}>Log Count</button>;
};

注意事项

  1. 性能权衡

    • 不要过度使用 React.memouseCallback,它们也有开销,适用于有明显性能问题的场景。
  2. 浅比较限制

    • React.memo 只进行浅比较,传递复杂对象时可能需要自定义比较函数。
  3. 依赖列表维护

    • useCallbackuseMemo 中正确设置依赖,避免错误或不必要的更新。

4. 为什么需要优化 React Native 的启动时间?如何进行优化?

优化 React Native 应用的启动时间是提升用户体验的关键因素,尤其在移动设备上,用户对应用响应速度非常敏感。以下是优化启动时间的原因及具体方法。


为什么需要优化启动时间?

  1. 用户体验

    • 如果应用加载时间过长,用户可能会流失或感到沮丧。数据显示,超过 3 秒的启动时间会显著增加用户放弃应用的概率。
  2. 竞争优势

    • 快速启动的应用更容易吸引用户,尤其在同类应用中,性能是一个显著的优势。
  3. 系统资源限制

    • 启动时间长会占用更多设备资源,影响低端设备或运行多任务时的性能。
  4. 提升品牌形象

    • 快速启动让用户感到应用更专业、可靠,从而提升品牌的整体形象。

如何优化 React Native 的启动时间?

1. 启用 Hermes 引擎

Hermes 是 Facebook 为 React Native 开发的 JavaScript 引擎,能显著减少应用启动时间,尤其是 Android。

操作步骤
  1. 在 React Native 项目中启用 Hermes:
bash 复制代码
npx react-native init MyApp

修改 android/app/build.gradle 文件:

gradle 复制代码
project.ext.react = [
   enableHermes: true  // 启用 Hermes
]
  1. 重新构建应用:
bash 复制代码
cd android && ./gradlew clean && cd ..
npx react-native run-android
优势
  • 更快的 JavaScript 初始化时间。
  • 更低的内存使用。

2. 减少 JavaScript 包大小

  • 代码分割

    • 将 JavaScript 拆分为多个小文件,并按需加载功能。
    • 使用动态 import() 方法按需加载模块。
  • 移除未使用的代码

  • 压缩和混淆代码

    • 使用 Metro Bundler 自带的压缩功能减少 JavaScript 包大小。
    • 启用生产模式:
bash 复制代码
npx react-native run-android --variant=release

3. 延迟加载资源

  • 懒加载组件
    • 对不需要立即显示的组件和功能进行懒加载。
jsx 复制代码
const LazyComponent = React.lazy(() => import('./LazyComponent'));
const App = () => (
  <React.Suspense fallback={<Text>Loading...</Text>}>
    <LazyComponent />
  </React.Suspense>
);
  • 按需加载图片
    • 使用低分辨率占位符,加载后再替换为高分辨率图片。

4. 优化原生模块加载

  • 移除未使用的原生模块

    • 确保只链接实际需要的原生模块,减少原生代码加载时间。
    • 使用 react-native-clean-project 清理项目中的多余依赖。
  • 合并原生模块初始化

    • 如果多个模块需要初始化,尽量将初始化过程合并到一个方法中。

5. 减少主线程阻塞

  • 优化渲染性能

    • 避免在启动时进行复杂计算,将其推迟到后台线程处理。

    • 使用 InteractionManager.runAfterInteractions 延迟非关键任务:

      jsx 复制代码
      InteractionManager.runAfterInteractions(() => {
        // 非关键任务
      });
  • 优化启动动画

    • 在应用启动时提供即时的启动屏幕或动画,减少用户感知的等待时间。

6. 使用原生启动屏幕

React Native 应用在启动时,可能需要加载 JavaScript 和原生模块。通过添加启动屏幕,可以在后台加载资源时向用户展示品牌形象。

操作步骤
  1. 使用库如 react-native-splash-screen
  2. 在启动时加载资源,完成后隐藏启动屏幕。

7. 减少依赖项

移除不必要的依赖,尤其是会增加启动时间的庞大库。例如:

  • 替换 moment.js(大体积)为 dayjs
  • 避免直接使用未优化的第三方库,如未按需加载的组件库。

8. 监控启动性能

使用性能监控工具检测启动时间的瓶颈:

  • React Native Performance Monitor:在开发环境中启用性能监控。
  • Flipper:检查网络请求、内存占用、组件加载时间。
  • Android Studio Profiler / Xcode Instruments:分析原生层面的性能问题。

5. 什么是 React Native 的 bridge,它会影响性能吗?

什么是 React Native 的 Bridge?

在 React Native 中,Bridge 是一个关键概念,用于连接 JavaScript 代码(运行在 JavaScript 引擎中,如 Hermes 或 JSC)和原生代码(运行在 Android 和 iOS 平台上)。

React Native 的核心架构如下:

  1. JavaScript 线程:运行 React 和应用逻辑(UI 渲染逻辑、状态管理等)。
  2. Bridge:一个通信层,用于在 JavaScript 线程和原生线程之间传递数据。
  3. 原生线程:负责原生视图的渲染、动画、手势处理等。

Bridge 的作用

  • 它通过异步消息传递,让 JavaScript 代码调用原生模块(如摄像头、地理定位)。
  • 同样,原生模块也可以通过 Bridge 把数据传回 JavaScript。

通信过程

  1. JavaScript 调用原生模块(通过 Bridge)。
  2. 数据打包为 JSON 格式。
  3. 通过异步消息队列传递到原生线程。
  4. 原生模块处理请求,并将结果通过 Bridge 发送回 JavaScript。

Bridge 对性能的影响

由于 React Native 的 Bridge 是异步的消息通道,其性能可能受到以下因素的影响:

1. 通信延迟
  • 每次跨越 JavaScript 和原生层的通信,都会有一定的延迟。
  • 如果通信频率高(例如频繁更新 UI、滚动动画),Bridge 的性能瓶颈会导致卡顿。
2. 数据序列化开销
  • 传递数据时需要将 JavaScript 对象序列化为 JSON 格式,原生端需要将其反序列化。
  • 如果传递的数据量很大(如大图片、复杂数据结构),序列化和反序列化的开销会明显增加。
3. 高频调用问题
  • 高频事件(如动画帧、手势处理)如果依赖 Bridge,可能因为消息队列拥堵而导致掉帧。
  • 示例:如果滚动事件需要通过 JavaScript 处理,Bridge 可能成为性能瓶颈。
4. 多线程并发问题
  • React Native 中,JavaScript 和原生线程独立运行。虽然这是为了避免阻塞,但如果线程之间的消息同步不及时,可能出现性能问题。

如何优化 Bridge 性能?

1. 减少通信频率
  • 将高频事件的逻辑尽量放在原生代码中处理,减少依赖 JavaScript。
  • 优化滚动事件:使用原生实现滚动逻辑(如 FlatList 或 SectionList)。
2. 减少传输数据量
  • 传递必要的数据,避免大数据对象直接通过 Bridge。
  • 对数据进行压缩或拆分处理。
3. 使用批量处理
  • 如果需要传递多个事件或数据,可以合并成一个消息,通过一次 Bridge 调用完成。
  • 示例:将多次状态更新合并为一个批量更新。
4. 优化动画
  • 使用 React Native 的原生动画驱动(如 AnimatedLayoutAnimation),而不是依赖 JavaScript 驱动的动画。
5. 使用 JSI(JavaScript Interface)

React Native 的新架构(Fabric 和 Turbo Modules)通过 JSI 替代了传统的 Bridge。JSI 提供了更高效的同步通信机制,避免了数据序列化和异步队列的开销。

特点

  • 支持直接调用原生代码,无需 JSON 序列化。
  • 提升通信效率,尤其适合高频任务。

要启用 JSI,需要确保你的 React Native 版本支持 Fabric 和 Turbo Modules。

6. 性能监控
  • 使用工具(如 Flipper)监控 Bridge 的消息队列,分析通信延迟和数据传输量。
  • 找到频繁调用或大数据传输的关键点并优化。

总结

Bridge 的优缺点

  • 优点:异步机制让 JavaScript 和原生线程解耦,降低线程阻塞风险。
  • 缺点:通信延迟、数据序列化等开销可能影响性能,尤其是在高频通信场景下。

优化方向

  • 避免高频事件依赖 Bridge。
  • 使用批量传输减少通信次数。
  • 尽早迁移到支持 JSI 的新架构,进一步提升通信效率。

6. 如何减少 JavaScript 和原生模块之间的通信开销?

减少 JavaScript 和原生模块之间的通信开销是优化 React Native 应用性能的关键。以下是一些有效的策略和实践方法:


1. 减少通信频率

合并调用

  • 问题:频繁的单次通信会增加 Bridge 的开销。
  • 解决方案:将多次调用合并为一次批量调用。例如,合并状态更新或事件发送。
javascript 复制代码
// 示例:批量发送数据
const sendData = (dataList) => {
  NativeModules.MyNativeModule.sendBatchData(dataList);
};

本地化高频任务

  • 问题:高频任务(如滚动、动画)如果通过 Bridge 处理,可能导致卡顿。
  • 解决方案:将高频任务逻辑尽量放在原生代码中。

例如,使用原生实现动画和手势处理:

  • React Native 提供了 AnimatedLayoutAnimation 等可以利用原生线程处理动画的工具。
  • 使用原生组件(如 FlatList)处理滚动,而不是自定义滚动逻辑。

2. 减少数据传输量

传递必要的数据

  • 问题:传递大数据对象或冗余数据会增加序列化和反序列化的时间。
  • 解决方案:只传递关键数据,避免整个对象通过 Bridge。
javascript 复制代码
// 示例:仅传递必要字段
const sendUserData = (user) => {
  NativeModules.MyNativeModule.sendUserData({
    id: user.id,
    name: user.name,
  });
};

数据压缩

  • 问题:数据量过大可能导致性能瓶颈。
  • 解决方案:在 JavaScript 层对数据进行压缩,再通过 Bridge 传递。

例如,将 JSON 数据压缩成字符串:

javascript 复制代码
import { gzip } from 'pako';

const compressedData = gzip(JSON.stringify(data));
NativeModules.MyNativeModule.sendData(compressedData);

3. 减少序列化和反序列化开销

使用轻量级数据结构

  • 避免使用嵌套过深的对象或数组,尽量使用扁平化的结构。
  • 示例:用简单的数组替代复杂的对象树。

迁移到 JSI(JavaScript Interface)

  • 问题:传统 Bridge 依赖 JSON 序列化和反序列化。
  • 解决方案:在支持新架构(Fabric 和 Turbo Modules)的 React Native 中使用 JSI。

特点

  • 直接调用原生代码,无需 JSON 序列化。
  • 提高数据传输效率,减少性能损耗。

4. 优化高频事件处理

减少事件监听器的绑定

  • 问题:过多的事件监听器可能增加通信负担。
  • 解决方案:优化事件监听逻辑,绑定必要的事件。
javascript 复制代码
// 示例:限制监听范围
useEffect(() => {
  const subscription = DeviceEventEmitter.addListener('eventName', handleEvent);
  return () => subscription.remove();
}, []);

延迟非关键任务

  • 将非关键任务延迟到后台线程处理,避免占用主线程时间。
javascript 复制代码
import { InteractionManager } from 'react-native';

InteractionManager.runAfterInteractions(() => {
  // 执行非关键任务
});

5. 使用原生模块的能力

原生端缓存

  • 问题:重复传输同样的数据会浪费带宽。
  • 解决方案:将需要频繁访问的数据缓存到原生端,避免重复通信。

事件触发机制

  • 使用原生端的事件触发机制(如 DeviceEventEmitter),通过事件监听代替主动通信。
javascript 复制代码
// 原生模块发送事件
DeviceEventEmitter.emit('customEvent', { key: 'value' });

// JavaScript 中监听
DeviceEventEmitter.addListener('customEvent', (data) => {
  console.log(data);
});

6. 性能监控和调优

监控 Bridge 开销

  • 使用工具如 FlipperReact Native Debugger 监控 Bridge 的通信频率和数据量。

识别瓶颈

  • 找出频繁调用的模块,分析哪些调用是可以合并或优化的。

7. 如何实现代码分割和动态加载?

在 React Native 中,代码分割和动态加载是优化性能、减少初始加载时间的关键技术。以下是实现代码分割和动态加载的详细指南:


1. 什么是代码分割和动态加载?

代码分割

将应用程序的代码分为多个小的独立块(chunks),以便按需加载所需的部分,而不是在启动时加载整个代码库。

动态加载

动态加载允许应用在运行时按需加载某些模块或组件,而不是在初始加载时加载所有代码。


2. 为什么要实现代码分割和动态加载?

  • 减少初始加载时间: 只加载启动时所需的代码,提高应用启动速度。

  • 按需加载: 延迟加载不常用的模块,提高运行时性能。

  • 内存优化: 避免一次性加载大块代码,减少内存占用。


3. 在 React Native 中实现代码分割和动态加载

(1)使用 React.lazy 实现组件的动态加载

React.lazy 是 React 内置的工具,可以用来按需加载组件。

示例:懒加载组件
javascript 复制代码
import React, { Suspense } from 'react';

// 动态加载组件
const LazyComponent = React.lazy(() => import('./LazyComponent'));

const App = () => (
  <Suspense fallback={<Text>Loading...</Text>}>
    <LazyComponent />
  </Suspense>
);

export default App;

注意

  • React.lazy 需要配合 Suspense 使用,fallback 属性用于在组件加载时显示占位内容。
  • 在 React Native 中,Suspense 的支持有限,尤其是某些场景下的服务端渲染。

(2)按需加载模块

动态加载函数或模块,可以避免一次性加载所有代码。

示例:动态加载模块
javascript 复制代码
const loadModule = async () => {
  const module = await import('./SomeModule');
  module.default(); // 使用动态加载的模块
};

const App = () => (
  <Button title="Load Module" onPress={loadModule} />
);

export default App;

如果使用 React Navigation,可以延迟加载屏幕组件,避免加载所有屏幕的代码。

示例:延迟加载屏幕
javascript 复制代码
import React, { Suspense } from 'react';
import { createStackNavigator } from '@react-navigation/stack';

const Stack = createStackNavigator();

const LazyScreen = React.lazy(() => import('./LazyScreen'));

const App = () => (
  <Stack.Navigator>
    <Stack.Screen
      name="LazyScreen"
      component={() => (
        <Suspense fallback={<Text>Loading Screen...</Text>}>
          <LazyScreen />
        </Suspense>
      )}
    />
  </Stack.Navigator>
);

export default App;

(4)使用 Metro Bundler 的分包功能

Metro 是 React Native 默认的打包工具,支持代码分包功能。

实现分包
  1. 配置分包 : 创建一个自定义的 metro.config.js 文件。
javascript 复制代码
const path = require('path');

module.exports = {
 resolver: {
   // 指定额外的包作为单独的依赖分包
   extraNodeModules: {
     someDependency: path.resolve(__dirname, 'node_modules/some-dependency'),
   },
 },
 transformer: {
   // 启用分包功能
   getTransformOptions: async () => ({
     transform: {
       experimentalImportSupport: true,
       inlineRequires: false,
     },
   }),
 },
};
  1. 创建分包入口: 将某些模块配置为单独的分包。

(5)使用动态加载的图片和资源

在 React Native 中,大型图片资源也可以延迟加载。

示例:动态加载图片
javascript 复制代码
const LazyImage = ({ uri }) => {
  const [loaded, setLoaded] = React.useState(false);

  return (
    <View>
      {!loaded && <ActivityIndicator />}
      <Image
        source={{ uri }}
        onLoad={() => setLoaded(true)}
        style={{ width: 100, height: 100 }}
      />
    </View>
  );
};

(6)避免加载无关依赖

通过 Babel 插件(如 babel-plugin-import)按需加载库的模块,减少无关代码的加载。

示例:按需加载 Ant Design Mobile
bash 复制代码
npm install babel-plugin-import --save-dev

配置 .babelrc

json 复制代码
{
  "plugins": [
    ["import", { "libraryName": "antd-mobile", "libraryDirectory": "es", "style": true }]
  ]
}

然后只加载需要的模块:

javascript 复制代码
import { Button } from 'antd-mobile'; // 只加载 Button 组件

4. 注意事项

  1. 适配性能监控工具

    • 使用工具如 Flipper 监控代码加载情况,确保延迟加载部分按预期执行。
  2. 处理动态加载的错误

    • 为动态加载添加错误边界,防止加载失败导致崩溃。
javascript 复制代码
class ErrorBoundary extends React.Component {
 state = { hasError: false };

 static getDerivedStateFromError(error) {
   return { hasError: true };
 }

 render() {
   if (this.state.hasError) {
     return <Text>Something went wrong!</Text>;
   }
   return this.props.children;
 }
}

const App = () => (
 <ErrorBoundary>
   <Suspense fallback={<Text>Loading...</Text>}>
     <LazyComponent />
   </Suspense>
 </ErrorBoundary>
);
  1. 测试用户体验
    • 确保延迟加载的占位符(如 fallback)能提供良好的用户体验。
相关推荐
当时只道寻常6 分钟前
Vue3 集成 NProgress 进度条:从入门到精通
前端·vue.js
kyriewen7 分钟前
React性能优化:从“卡成狗”到“丝般顺滑”的5个秘诀
前端·react.js·性能优化
米丘7 分钟前
Vue 3.x 单文件组件(SFC)模板编译过程解析
前端·vue.js·编译原理
helloweilei9 分钟前
Web Streams 简介
前端·javascript
悟空瞎说9 分钟前
Flutter热更新 Shorebird CodePush 原理、实现细节及费用说明
前端·flutter
didadida26210 分钟前
从“不存在”的重复请求,聊到 Web 存储的深坑
前端
xiaominlaopodaren11 分钟前
Three.js 渲染原理-透明渲染为什么这么难
前端
云南瑞达工程材料有限公司12 分钟前
云南生态袋;云南园林生态袋;昆明河道治理生态袋;边坡生态袋
面试
米丘12 分钟前
vue3.x 内置指令有哪些?
前端·vue.js