0Pricing
React Native Academy · บทเรียน

ขนาดบันเดิลและการโหลดแบบขี้เกียจ

วิเคราะห์บันเดิล JS ด้วย react-native-bundle-visualizer ระบุการพึ่งพาขนาดใหญ่ ใช้ dynamic import() สำหรับหน้าจอที่มีขนาดมาก และวัดขนาดบันเดิลก่อนและหลัง

ขนาดบันเดิลและการโหลดแบบขี้เกียจ เป็นบทเรียน React Native Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน 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 ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส React Native Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส React Native Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “ขนาดบันเดิลและการโหลดแบบขี้เกียจ”

วิเคราะห์บันเดิล JS ด้วย react-native-bundle-visualizer ระบุการพึ่งพาขนาดใหญ่ ใช้ dynamic import() สำหรับหน้าจอที่มีขนาดมาก และวัดขนาดบันเดิลก่อนและหลัง คุณปฏิบัติ React Native Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน React Native Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน React Native Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 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