Làm app di động với React Native và Expo, từ cài đặt đến kiểm thử E2E

From a clean Android setup to a tested React Native app built with Expo

Một trang, đi từ cài Android Studio và adb, qua dự án FocusList, đến test tự động bằng Maestro trên emulator.

React Native / JS Expo / EAS Android / native Kiểm thử / Testing Cảnh báo / Gotcha

Cơ bản Basics ●○○ Beginner · Cơ bản

Nếu bạn đã biết React cho web, bạn đã có 70% kiến thức. Phần còn lại là đổi bộ “gạch xây nhà”.

Core component

Core components

Không có div hay p. Bạn dùng View, Text, Image, TextInput, Pressable, FlatList. Mỗi cái được vẽ bằng view native thật.

StyleSheet và Flexbox

Styling & layout

Style viết bằng object JS (backgroundColor, không phải background-color). Mọi thứ dàn bằng Flexbox, và hướng mặc định là dọc (column).

State và hook

State & hooks

Giống hệt React web: useState, useEffect. Đổi state thì giao diện tự cập nhật.

Expo

Framework and toolchain

Expo là bộ công cụ và thư viện bao quanh React Native: tạo dự án, chạy thử, build, cập nhật. Nó giúp bạn khỏi phải tự cấu hình phần native.

Expo Router

File-based navigation

Giống App Router của Next.js: file trong thư mục app/ thành màn hình. app/add.tsx thành route /add.

Chạy trên máy thật

Real devices & emulators

Code của bạn chạy bằng engine JS (Hermes) trong app native. Bạn xem kết quả trên điện thoại thật hoặc emulator Android.

Mẹo nhớ Memory tips
  • React Native = phiên dịch viên. Bạn nói React, nó dịch sang tiếng Android và iOS. You speak React, it speaks native.
  • Expo = chung cư có sẵn tiện ích. Điện nước đã có, bạn chỉ lo nội thất. Khi cần xây thêm, bạn vẫn có thể “cơi nới” bằng dev build.
  • Flexbox RN: “mặc định xếp cột”. Web thì mặc định xếp hàng. Web is row, RN is column.

Web so với React Native Web vs React Native

WebReact NativeTrên Android thành / Becomes on Android
divViewViewGroup
p, span, h1Text (chữ luôn phải nằm trong Text)TextView
imgImageImageView
inputTextInputEditText
button, onclickPressable, onPressView có nhận chạm
ul + liFlatListDanh sách tái sử dụng view
CSS pxSố không đơn vị = dpdensity-independent pixel
Hiểu lầm thường gặp Common misconceptions
  • “React Native là web được đóng gói trong WebView.” Sai: UI được vẽ bằng view native.
  • “Dùng Expo thì bị giới hạn.” Chưa đúng: Expo Go có giới hạn, nhưng dev build cho phép thêm code native.
  • “Expo Go chính là app của tôi.” Sai: Expo Go là một app có sẵn để chạy thử, không phải bản phát hành.
  • “Phải cài Android Studio mới thử được.” Không: chạy bằng Expo Go trên điện thoại thật thì không cần (xem phần Cài đặt).

Cài đặt môi trường Environment setup ●○○ Beginner · Cơ bản

Bước này hay làm người mới nản nhất. Hãy chọn đúng mục tiêu để chỉ cài thứ bạn cần.

Bạn muốn làm gì? Pick your goal

Cài Android Studio và SDK Android Studio & SDK

  1. Cài Node.js bản LTS (kiểm tra bằng node -v).
  2. Cài Android Studio. Lần chạy đầu, chọn bản cài Standard để nó tải SDK cơ bản.
  3. Mở More Actions → SDK Manager (hoặc Settings → Languages & Frameworks → Android SDK) rồi đánh dấu các mục trong bảng dưới.
  4. Mở Device Manager, tạo một thiết bị ảo (AVD) và chạy thử.
  5. Đặt biến môi trường ANDROID_HOME và thêm thư mục công cụ vào PATH (dùng công cụ bên dưới).
Mục trong SDK ManagerDùng để làm gì What forBắt buộc?
Android SDK Platform (một API level gần đây)Thư viện để biên dịch app cho phiên bản Android đóCó
Android SDK Platform-ToolsChứa adb để nói chuyện với thiết bịCó
Android SDK Build-ToolsCông cụ đóng gói APK/AAB, Gradle gọi tớiCó (khi build local)
Android EmulatorChạy máy ảoNếu dùng emulator
System Image (đúng kiến trúc máy bạn)“Hệ điều hành” cho máy ảo: x86_64 cho Intel/AMD, arm64 cho chip Apple SiliconNếu dùng emulator
Command-line ToolsCông cụ dòng lệnh như sdkmanagerNên có

Dự án React Native yêu cầu một bản JDK cụ thể (tài liệu hiện nay khuyên JDK 17, hãy kiểm tra lại cho phiên bản bạn dùng). JDK đi kèm Android Studio thường dùng được.

Sinh lệnh biến môi trường Environment variable helper

Đừng dùng setx để sửa PATH trên Windows. Lệnh này có thể cắt ngắn PATH dài. Hãy sửa trong Environment Variables bằng giao diện rồi mở lại terminal.

Checklist tiến độ Setup checklist

Bật chế độ nhà phát triển trên điện thoại thật Real device

  1. Cài đặt → Thông tin điện thoại, chạm Số bản dựng (Build number) 7 lần.
  2. Vào Tuỳ chọn nhà phát triển, bật Gỡ lỗi USB (USB debugging).
  3. Cắm cáp, chấp nhận hộp thoại tin cậy máy tính, rồi chạy adb devices. Máy phải hiện trạng thái device.

Tên menu thay đổi theo hãng máy (Samsung, Xiaomi, Oppo...).

Bộ lệnh adb cần nhớ adb cheat sheet

LệnhCông dụng
adb devicesLiệt kê thiết bị và emulator đang kết nối
adb reverse tcp:8081 tcp:8081Cho thiết bị nối tới Metro trên máy bạn qua cáp USB
adb logcat *:EXem log lỗi của thiết bị
adb install -r app.apkCài (hoặc cài đè) file APK
adb shell pm clear com.example.focuslistXoá dữ liệu app, hữu ích để reset trước khi test
adb shell input keyevent 82Mở menu nhà phát triển của app
adb exec-out screencap -p > shot.pngChụp màn hình thiết bị
adb kill-server rồi adb start-serverKhởi động lại dịch vụ adb khi nó “đơ”

Gỡ lỗi cài đặt Troubleshooting

Triệu chứngNguyên nhân thường gặpCách xử lý
adb is not recognizedChưa thêm platform-tools vào PATHThêm đường dẫn, mở lại terminal
SDK location not foundThiếu ANDROID_HOMEĐặt biến, hoặc ghi sdk.dir=... vào android/local.properties
Thiết bị hiện unauthorizedChưa chấp nhận khoá RSAMở khoá máy, chấp nhận hộp thoại; nếu không có, thu hồi quyền gỡ lỗi USB rồi cắm lại
Emulator rất chậm hoặc không lênChưa bật ảo hoá, hoặc system image sai kiến trúcBật virtualization trong BIOS/OS, chọn image khớp CPU
App trắng/đỏ, không tìm thấy MetroThiết bị không thấy máy tínhadb reverse tcp:8081 tcp:8081, hoặc cùng Wi-Fi, hoặc npx expo start --tunnel
Gradle báo sai phiên bản JavaJAVA_HOME trỏ JDK khácTrỏ tới JDK được yêu cầu (thường là JDK đi kèm Android Studio)
Port 8081 đã bị dùngCó Metro khác đang chạyTắt tiến trình cũ hoặc chọn cổng khác khi chạy

Công cụ chẩn đoán nhanh: npx expo-doctor kiểm tra cấu hình dự án Expo.

Tương tác Hands-on ●○○ Beginner · Cơ bản

Một cú chạm đi đâu? What happens on a tap

Bấm Bước tiếp hoặc Tự chạy để đi qua từng giai đoạn. Mặc định đang tạm dừng.

Sơ đồ khái niệm của kiến trúc mới, không theo tỉ lệ thời gian. Conceptual diagram, not to scale.

Sân chơi Flexbox Flexbox playground

Thử thay đổi và so với mặc định của React Native.

1
2
3

Dự án mẫu: FocusList Sample project ●○○ Beginner · Cơ bản

Ứng dụng danh sách việc cần làm: xem danh sách, thêm việc mới, lưu lại trên máy. Dùng TypeScript và Expo Router.

    Trung cấp Intermediate ●●○ Intermediate · Trung cấp

    Expo Go, dev build và bản phát hành Three ways to run

    Tiêu chíExpo GoDevelopment buildBản phát hành (EAS)
    Là gìApp có sẵn trên cửa hàng, nạp dự án của bạnApp riêng của bạn, kèm công cụ devBản gửi lên cửa hàng
    Thư viện native tuỳ chỉnhKhôngCóCó
    Cần Android Studio?KhôngBuild trên máy: có. Build trên EAS: khôngKhông nếu build trên EAS
    Dùng khiHọc, thử nhanhPhát triển hằng ngày khi cần nativePhát hành, test E2E sát thực tế

    Expo Router: file thành màn hình File → route

    FileRouteGhi chú
    app/index.tsx/Màn hình đầu
    app/add.tsx/addMột màn hình khác
    app/task/[id].tsx/task/42Đoạn động, đọc bằng useLocalSearchParams()
    app/_layout.tsx(khung bọc)Đặt Stack hoặc Tabs ở đây
    app/(tabs)/...(nhóm route)Ngoặc tròn nhóm file mà không đổi URL

    Máy tính dp và pixel dp ↔ px calculator

    React Native dùng dp (density-independent pixel): cùng một số, kích thước nhìn gần như nhau trên mọi màn hình. Số pixel thật bằng dp nhân tỉ lệ mật độ.

    Công thức: px = dp × density. Kích thước vật lý tính theo định nghĩa lý thuyết 1 dp = 1/160 inch. Thiết bị thật sai lệch nhất định. Trong code, PixelRatio.get() trả về tỉ lệ này.

    Các ô đúng tỉ lệ với nhau về số pixel. Chúng không phải kích thước thật trên màn hình của bạn. 48 dp là cỡ tối thiểu thường được khuyến nghị cho vùng chạm trên Android (iOS thường nhắc 44 pt).

    Danh sách hiệu quả Lists

    Kiểm thử đơn vị với Jest Unit tests

    Kiểm thử component nhanh, không cần emulator.

    
    
    
    

    Kiểm thử E2E trên di động Mobile E2E testing ●●○ Intermediate · Trung cấp

    E2E (end-to-end) mở app thật trên emulator hoặc máy thật, rồi bấm như người dùng: đăng nhập, thêm việc, kiểm tra kết quả.

    Kim tự tháp kiểm thử Testing pyramid

    Minh hoạ, không theo tỉ lệ. Illustrative, not to scale. Quy tắc: viết ít E2E, chỉ cho các hành trình quan trọng (đăng nhập, thanh toán, thao tác chính).

    Cách một bài E2E hoạt động How E2E works

    Sơ đồ khái niệm, không theo tỉ lệ. Chi tiết bên trong từng công cụ khác nhau.

    Chọn công cụ nào? Choosing a tool

    Tiêu chíMaestroDetoxAppium
    Cách viết testFile YAMLJavaScript/TypeScript với JestWebDriver, nhiều ngôn ngữ
    KiểuBlack-boxGray-box, đồng bộ với appBlack-box
    Cài đặtNhẹ, một CLINặng hơn, phải cấu hình build nativeCần server và driver
    Độ ổn địnhTự chờ giao diện ổn địnhTự đồng bộ với animation và mạngBạn thường tự viết đoạn chờ
    Hợp vớiBắt đầu nhanh, QA ít codeĐội JS cần kiểm soát sâuĐội đa ngôn ngữ, farm thiết bị

    Đánh giá chung, có thể thay đổi theo từng phiên bản. Hãy đọc tài liệu mới nhất trước khi chọn.

    Hướng dẫn từng bước với Maestro Step by step with Maestro

      Trình dựng flow Maestro Flow builder

      Chọn lệnh, nhập giá trị, bấm thêm. Mẫu YAML hiện ngay bên dưới.

      
      

      Detox, tóm tắt Detox in short

      
      

      Detox cần cấu hình build native (.detoxrc.js). Với Expo, bạn cần dev build và config plugin; hãy làm theo tài liệu hiện hành của Detox và Expo.

      Chạy trên CI CI example

      
      
      Gotcha: bản debug tải JS từ Metro, nên trên CI hãy dùng bản release hoặc profile preview của EAS (đã đóng gói sẵn JS). Phiên bản action và cú pháp ở trên chỉ để minh hoạ, hãy kiểm tra lại.

      Mẹo để test bớt “flaky” Reducing flakiness

      Nâng cao Advanced ●●● Advanced · Nâng cao

      Kiến trúc mới New Architecture

      Kiến trúc cũ gửi thông điệp JSON bất đồng bộ qua “bridge”. Kiến trúc mới dùng JSI để JS gọi thẳng vào C++, cùng Fabric (vẽ giao diện) và TurboModules (module native nạp khi cần). Từ các bản gần đây nó là mặc định cho dự án mới, nhưng thời điểm và mức hỗ trợ kiến trúc cũ thay đổi giữa các bản, hãy xem ghi chú phát hành của phiên bản bạn dùng.

      Ngân sách thời gian mỗi khung hình Frame budget

      Thang đo đúng tỉ lệ: ms = 1000 ÷ fps, làm tròn. Muốn mượt, công việc cho mỗi khung hình phải xong trong ngần ấy thời gian.

      Prebuild và config plugin CNG

      Với Continuous Native Generation (CNG), thư mục android/ và ios/ được sinh ra từ app.json cộng các config plugin, nên bạn không phải commit code native.

      
      
      

      Phát hành bằng EAS Shipping with EAS

      
      
      
      

      Hiệu năng Performance

      Bảo mật Security

      Dòng thời gian Timeline

      Mốc năm làm tròn và có thể lệch một chút tuỳ nguồn. Số phiên bản mới nhất của RN và Expo SDK thay đổi nhiều lần mỗi năm, hãy xem trang chính thức.

      Thực hành tốt nhất Best practices

      Câu hỏi còn mở Open questions

      Từ vựng Vocabulary ●●○ Mọi cấp / All levels

      EnglishTiếng ViệtExample sentence

      Mini game Review quiz ●●○ 20 câu / questions

      5 điểm mỗi câu đúng, từ câu đúng thứ 3 liên tiếp được thêm 2 điểm thưởng chuỗi. Điểm tối đa là 136.