React Native Academy · 课时

包体积与延迟加载

使用 react-native-bundle-visualizer 分析 JS 包,找出体积较大的依赖项,对大型屏幕应用动态 import(),并衡量优化前后的包体积。

第 4 / 4 课13 个步骤

包体积与延迟加载 是 CoddyKit 上的免费 React Native Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 React Native Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 React Native Academy 课程共包含 4 节课。

本课时的部分内容尚未翻译,以英文显示。

Why Bundle Size Affects Startup Time

The JavaScript bundle is the single file that contains all your app's code and most third-party libraries. Hermes parses and executes this file on every app cold start. A larger bundle means a longer startup time — even a 500KB increase can add hundreds of milliseconds to your time-to-first-interaction on mid-range Android devices.

Bundle size optimization is especially important in React Native because all JavaScript must load before the first screen can render. Unlike a web app where you can lazy-load route chunks before rendering, React Native apps pay the full bundle cost upfront.

Hermes Bytecode and Bundle Measurement

When Hermes is enabled (the default since RN 0.70), the JavaScript bundle is pre-compiled to bytecode at build time. This reduces parse time but the raw bundle size still affects how much bytecode the engine must load.

Measure your current bundle size by running the Metro bundler with the --bundle-output flag and checking the output file size. Compare the bundle before and after changes to measure the impact of your optimizations.

# Generate the bundle and measure its size
npx react-native bundle \
  --platform android \
  --dev false \
  --entry-file index.js \
  --bundle-output /tmp/bundle.js

# Check the size
ls -lh /tmp/bundle.js
# e.g., -rw-r--r-- 1 user group 2.4M /tmp/bundle.js

react-native-bundle-visualizer

react-native-bundle-visualizer generates a treemap visualization of your bundle, showing exactly which packages and modules take up the most space. Install it globally and run it against your project to see the breakdown.

Look for large packages in the treemap that might be replaceable with lighter alternatives. Common culprits include moment.js (replace with date-fns), lodash (import individual functions), and large icon libraries (use only the icons you need).

# Install globally
npm install -g react-native-bundle-visualizer

# Run from your project root
npx react-native-bundle-visualizer

# Opens a browser with an interactive treemap showing:
# - Each library's size
# - Which modules within each library are included
# - Total bundle composition breakdown

Tree Shaking and Named Imports

Tree shaking is the process where the bundler removes unused code from the final bundle. For tree shaking to work, you must import only what you use from libraries — using named imports instead of importing the entire default export.

Metro (React Native's bundler) performs basic tree shaking for ES modules. Libraries must use ES module syntax (export / import) for tree shaking to be effective. CommonJS (module.exports) modules are not tree-shakable.

// ❌ Imports the entire lodash library (~70KB)
import _ from 'lodash';
const sorted = _.sortBy(items, 'name');

// ✅ Imports only sortBy (~3KB)
import sortBy from 'lodash/sortBy';
const sorted = sortBy(items, 'name');

// ❌ Imports all of lucide-react-native icons
import * as Icons from 'lucide-react-native';

// ✅ Imports only the used icons
import { Home, Settings, User } from 'lucide-react-native';

Dynamic Import for Lazy Loading

React supports dynamic import with import() to load a module asynchronously at runtime instead of including it in the initial bundle. Combined with React.lazy and Suspense, you can defer loading heavy screens until the user actually navigates to them.

This splits the bundle into chunks. The initial bundle stays small and loads quickly. Heavy screens like a PDF viewer, a video editor, or a complex chart library are only downloaded when needed.

import React, { lazy, Suspense } from 'react';
import { ActivityIndicator } from 'react-native';

// Heavy screen loaded only when the user navigates to it
const ReportScreen = lazy(() => import('./screens/ReportScreen'));

export function AppNavigator() {
  return (
    <Stack.Navigator>
      <Stack.Screen name='Home' component={HomeScreen} />
      <Stack.Screen
        name='Report'
        component={() => (
          <Suspense fallback={<ActivityIndicator />}>
            <ReportScreen />
          </Suspense>
        )}
      />
    </Stack.Navigator>
  );
}

Replacing Heavy Libraries with Lightweight Alternatives

Sometimes the best bundle optimization is swapping a heavy library for a smaller one. Common replacements:

  • moment.js (67KB) → date-fns (imported per function, ~3KB) for date formatting
  • lodash (full, 70KB) → individual lodash imports or native JS methods
  • axios (12KB) → ky (3KB) or native fetch for simple use cases

Before replacing a library, verify the alternative covers all your use cases to avoid surprises in edge cases.

// Replace moment.js with date-fns
// Before (adds ~67KB to bundle):
import moment from 'moment';
const formatted = moment(date).format('MMM DD, YYYY');

// After (adds ~3KB for just this function):
import { format } from 'date-fns';
const formatted = format(new Date(date), 'MMM dd, yyyy');

Inline Requires for Module Loading

Metro supports inline requires, a Babel plugin that moves module imports to the first point of use instead of the top of the file. This delays the execution of module code until it is actually needed, improving cold start time even without true code splitting.

Enable inline requires in your metro.config.js or babel.config.js. This is one of the easiest wins for startup time — Meta reports it improving startup by 25-40% on large apps like Facebook.

// babel.config.js
module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: [
    'transform-inline-requires', // Delays module evaluation
  ],
};

// metro.config.js alternative:
module.exports = {
  transformer: {
    getTransformOptions: async () => ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: true,
      },
    }),
  },
};

Removing Unused Packages

Over time, projects accumulate dependencies that are no longer used. Tools like depcheck scan your codebase and report packages listed in package.json but never imported. Removing these shrinks the bundle and speeds up install times.

Run npx depcheck and review the output carefully. Some packages are used indirectly (as peer dependencies or via config files), so check before removing them. After cleanup, run a full build to confirm nothing broke.

# Check for unused dependencies
npx depcheck

# Output example:
# Unused dependencies:
# * react-native-camera (replaced by expo-camera)
# * lodash (replaced by individual imports)
# * @types/moment (moment was removed)

# Remove them:
npm uninstall react-native-camera lodash @types/moment

Analyzing Build Size by Platform

For iOS, open the .ipa file (it is a ZIP) and measure Payload/YourApp.app/main.jsbundle. For Android, open the .aab or .apk with bundletool or the Android Studio APK Analyzer to see the size breakdown.

EAS Build provides a bundle size history in the Expo dashboard, making it easy to track size regressions across builds. Set up a CI check that fails if the bundle size grows beyond a threshold to catch accidental size increases.

# Unzip the IPA to measure iOS bundle
unzip -o MyApp.ipa -d /tmp/ipa-contents
ls -lh '/tmp/ipa-contents/Payload/MyApp.app/main.jsbundle'

# Android APK Analyzer from the command line
# (Android Studio → Build → Analyze APK)
# Or use bundletool:
bundletool dump resources --bundle=app.aab

Measure Before Releasing

Before each production release, add bundle size measurement to your CI pipeline. A simple size check in a GitHub Actions workflow can flag when a pull request adds a significant amount of bytes to the bundle.

Combine size monitoring with app startup timing using Firebase Performance Monitoring's automatic trace for app_start. This gives you data from real devices in production, showing whether your optimizations are working for users on slower hardware.

# Example GitHub Actions step to fail if bundle exceeds 3MB
- name: Check bundle size
  run: |
    npx react-native bundle \
      --platform android \
      --dev false \
      --entry-file index.js \
      --bundle-output /tmp/bundle.js
    SIZE=$(wc -c < /tmp/bundle.js)
    MAX=3145728  # 3MB
    if [ $SIZE -gt $MAX ]; then
      echo "Bundle too large: $SIZE bytes (limit: $MAX)"
      exit 1
    fi

Profiling Startup Time

Bundle size is one factor in startup time; another is how much JS executes before the first screen renders. Use the Hermes Debugger Timeline in Flipper to record a cold start and see which modules take the longest to initialize.

Look for any synchronous module side effects — code at the top level of a module that runs when the module is first imported. Move expensive initialization into lazy functions called on demand, not at import time.

// ❌ Side effect at module level — runs at import time
const data = expensiveComputation(); // Blocks startup
export default function MyModule() { ... }

// ✅ Lazy initialization — only runs when called
let data: any = null;
function getdata() {
  if (!data) data = expensiveComputation();
  return data;
}
export default function MyModule() {
  const d = getdata(); // Called on first use, not at import
  ...
}

Quick Check

Test your understanding of React Native Mobile Development concepts from this lesson.

Lesson Recap

In this lesson you learned: how to measure bundle size and visualize its composition with react-native-bundle-visualizer, how named imports and tree shaking reduce unnecessary code in the bundle, and how React.lazy and dynamic import defer loading heavy screens until they are needed. Next up we set up Jest and write our first unit tests for React Native.

免费开始

用 AI 导师学习 JavaScript — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
30
课程
120

常见问题解答

「包体积与延迟加载」课时是免费的吗?

是的 — 「包体积与延迟加载」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 React Native Academy 课程的其余内容,请升级到 CoddyKit PRO。 React Native Academy 课程共包含 4 节课。

「包体积与延迟加载」这节课中我会学到什么?

使用 react-native-bundle-visualizer 分析 JS 包,找出体积较大的依赖项,对大型屏幕应用动态 import(),并衡量优化前后的包体积。 你通过在浏览器中直接运行的动手代码来练习 React Native Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 React Native Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 React Native Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「包体积与延迟加载」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 React Native Academy 课中编写并运行代码吗?

能。每节 React Native Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 使用 Flipper 与 React DevTools 进行性能分析
  2. 使用 React.memo、useCallback 与 useMemo 进行记忆化
  3. FlatList 性能调优
  4. 包体积与延迟加载
← 返回 React Native Academy