Expo 環境建置與 RN 核心元件
如果你已經會寫 HTML、CSS 和 React(或是 Next.js),那麼恭喜你,你已經具備了打造 iOS 和 Android 原生 App 的能力!
過去,寫 iOS App 必須學 Swift 和 Xcode,寫 Android 必須學 Kotlin 和 Android Studio。要維護兩個完全不同的程式碼庫,對獨立開發者來說是一場惡夢。
React Native (RN) 打破了這個藩籬。它允許你用 JavaScript 寫邏輯,然後底層會自動把這些程式碼編譯成真正原生的手機元件(而不是把網頁塞進 WebView 裡)。
而 Expo 則是建立在 RN 之上的神級框架。就像 Next.js 之於 React 一樣,Expo 幫你處理了所有最痛苦的底層設定,讓你能在幾分鐘內就在自己的手機上看到熱重載 (Hot Reload) 的 App 畫面!
1. 環境建置:只需一行指令
你不需要安裝幾十 GB 的 Xcode 或 Android Studio。你只需要 Node.js!
打開終端機,輸入:
# 建立一個新的 Expo 專案
npx create-expo-app@latest my-first-app
# 進入資料庫
cd my-first-app
# 啟動開發伺服器
npm run start
這時候,你的終端機會出現一個巨大的 QR Code。
如何在手機上預覽?
- 拿起你的 iPhone 或 Android 手機。
- 在 App Store / Google Play 搜尋並下載 Expo Go 這個 App。
- 如果是 iPhone,直接打開內建相機掃描電腦螢幕上的 QR Code;如果是 Android,打開 Expo Go 點擊 Scan QR Code。
神奇的事情發生了!你的手機畫面上出現了 App 的雛形。現在,只要你在電腦上存檔,手機畫面就會在不到一秒內瞬間更新,這就是傳說中的 Hot Reload!
2. 忘掉 HTML 標籤!認識原生元件
在 React Native 的世界裡,沒有 <div>, <span>, <p> 這些標籤,因為手機看不懂 HTML。
你必須從 react-native 中引入專屬的原生元件:
| Web (網頁) | React Native (手機) | 用途 |
| :--- | :--- | :--- |
| <div> | <View> | 容器、排版區塊 |
| <p> / <h1> / <span> | <Text> | 顯示所有文字 |
| <button> | <TouchableOpacity> / <Pressable> | 可點擊的按鈕 |
| <img> | <Image> | 顯示圖片 |
| <input> | <TextInput> | 文字輸入框 |
| <div style="overflow: scroll"> | <ScrollView> | 可滾動的視窗 |
讓我們來改寫 app/index.tsx (或是 App.js):
import { StyleSheet, Text, View, Image, TouchableOpacity, Alert } from 'react-native';
export default function App() {
const handlePress = () => {
// Alert.alert 會叫出手機系統原生的警告視窗!
Alert.alert("太棒了", "你成功點擊了按鈕!");
};
return (
<View style={styles.container}>
<Image
source={{ uri: 'https://reactnative.dev/img/tiny_logo.png' }}
style={styles.logo}
/>
<Text style={styles.title}>Hello Vibe Tutor!</Text>
<Text style={styles.subtitle}>這是我的第一個原生 App 📱</Text>
<TouchableOpacity style={styles.button} onPress={handlePress}>
<Text style={styles.buttonText}>點我測試</Text>
</TouchableOpacity>
</View>
);
}
3. 忘掉 CSS Class!認識 StyleSheet
在 RN 中,所有的樣式都是透過 JavaScript 物件來定義的,使用的是 Flexbox 佈局。
[!WARNING] 佈局地雷警告
- RN 的
flexDirection預設是column(直向排列),而網頁 CSS 預設是row(橫向)!- 所有尺寸都沒有單位(不能寫
px或rem),直接寫數字width: 100。- 不支援部分複雜的 CSS 屬性(如
box-shadow的寫法會被拆成好幾個屬性)。
我們來看看上面例子的樣式表:
const styles = StyleSheet.create({
container: {
flex: 1, // 佔滿整個螢幕
backgroundColor: '#fff',
alignItems: 'center', // 左右置中
justifyContent: 'center', // 上下置中
},
logo: {
width: 100,
height: 100,
marginBottom: 20,
},
title: {
fontSize: 24,
fontWeight: 'bold',
color: '#333',
},
subtitle: {
fontSize: 16,
color: '#666',
marginTop: 8,
marginBottom: 32,
},
button: {
backgroundColor: '#3b82f6',
paddingHorizontal: 24,
paddingVertical: 12,
borderRadius: 8,
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: '600',
}
});
[!TIP] 想要用 Tailwind CSS 嗎? 你絕對可以!透過安裝
NativeWind這個套件,你就可以在 RN 裡面寫<View className="flex-1 items-center justify-center bg-white">這種熟悉的語法了!這對 Web 開發者來說是極大的福音。
4. SafeAreaView:拯救你的瀏海與動態島
當你把上面的程式碼放到 iPhone 上執行時,你可能會發現文字被上面的「瀏海」或是「時間列」擋住了!
在手機開發中,我們必須尊重系統的「安全區域」。請養成好習慣,將最外層的容器換成 <SafeAreaView>:
import { SafeAreaView } from 'react-native-safe-area-context';
export default function App() {
return (
<SafeAreaView style={{ flex: 1, backgroundColor: 'white' }}>
<View style={styles.container}>
{/* ...內容... */}
</View>
</SafeAreaView>
);
}
這樣一來,不管你的 App 是跑在有瀏海的 iPhone 14、還是有動態島的 iPhone 15 Pro,你的 UI 都會被完美地推擠到安全的可視範圍內!
在下一章中,我們將學習如何打造 App 中最重要的骨幹:多頁面導覽 (Navigation)!