課程:RN 跨平台開發基礎 第 2 堂:核心元件與工具鏈
RN 核心元件剖析
想像一下,你正試圖把一段網頁的 HTML 程式碼貼到 React Native 專案中。你寫了一個 <div> 包著一個 <span>,然後點擊儲存。下一秒,你的模擬器立刻噴出了滿螢幕的紅字錯誤:「ReferenceError: Can't find variable: div」。
這就是每個從 Web 轉向 React Native 的開發者都會遇到的第一個震撼:在手機的世界裡,根本沒有 HTML。
在本節中,我們將深入探討 React Native 的核心元件。這些元件是你與 AI 協作、建構 APP 介面最基礎的磚塊。我們不只要學會怎麼用它們,更要理解它們背後對應的原生邏輯,以及為什麼它們的行為與瀏覽器中的標籤大相徑庭。
為什麼不能用 div?
在之前的課程中,我們建立了一個核心心智模型:JS 層是指揮官,原生層是執行部隊。
瀏覽器之所以懂 <div> 或 <span>,是因為它內建了渲染引擎(如 Chrome 的 Blink),能將這些標籤解析成 DOM(Document Object Model)樹。但手機作業系統(iOS 或 Android)的核心並沒有 DOM。如果你對著 iOS 說:「請給我畫一個 <div>」,它會一頭霧水,因為它只認識 UIView。
React Native 提供了一組「橋接元件」(Core Components)。當你在程式碼裡寫下 <View> 時,React Native 的橋接機制會發送指令給原生層:
- 在 iOS 上,它會命令系統實例化一個
UIView。 - 在 Android 上,它會命令系統實例化一個
android.view.View。
這就是為什麼你不能直接使用 HTML 標籤。React Native 的元件本質上是跨平台指令的代稱。
View:UI 的基礎容器
在 Web 開發中,我們習慣用 <div> 來做任何事情:排版、包裹元件、當作分隔線。在 React Native 中,<View> 就是那個萬能的 <div>。
原生對應
View 是所有 UI 的基礎。它是螢幕上的一個矩形區域,可以包含其他的子元件。
- iOS:
UIView - Android:
android.view.View
關鍵特性與設計邏輯
- Flexbox 排版:與 Web 不同,
View預設就啟用了 Flexbox 排版模型。在手機開發中,我們幾乎完全依賴 Flexbox。但有一個關鍵差異:RN 的View預設flexDirection是column(垂直排列),而不是 Web 的row。這是因為手機螢幕通常是縱向的。 - 不支援文字直接置入:在
<div>裡,你可以隨便寫一段文字。但在<View>裡,如果你直接放入字串,程式會立刻崩潰。這引出了我們下一個核心元件。
Text:文字的唯一居所
這可能是 Web 開發者最容易踩到的坑。在 React Native 中,所有字串必須被包裹在 **<Text>** 元件中。
為什麼這麼嚴格?
在網頁上,文字是 DOM 的一部分,可以自由地流動在各種標籤之間。但在原生開發中,文字的渲染是由專門的類別負責的(如 iOS 的 UILabel 或 UITextView)。原生層需要知道這是一段「文字」,才能正確套用字型、字級、行高等排版屬性。
Text 的特殊能力:樣式繼承
在 React Native 中,樣式通常是不繼承的。如果你在一個 View 上設定了 color: 'red',它裡面的 Text 元件並不會變紅。
但是,有一個唯一的例外:**<Text>**** 的嵌套**。
<Text style={{ fontWeight: 'bold' }}>
我是粗體
<Text style={{ color: 'red' }}> 我是紅色的粗體</Text>
</Text>
當你把一個 <Text> 放在另一個 <Text> 裡面時,內層的元件會繼承外層的樣式。這在處理「一段文字中某些字要變色或加粗」的需求(如使用者服務協議中的連結)時非常有用。
Image:不只是給一個 URL
在 Web 中,我們用 <img src="...">。在 React Native 中,我們使用 <Image source={...}>。雖然看起來只是屬性名稱變了,但背後的邏輯大有學問。
Source 的兩種模式
- 本地圖片:使用
require語法。
<Image source={require('./assets/logo.png')} />
這種方式在編譯時就會處理圖片路徑,確保圖片一定存在,且 RN 會自動根據螢幕密度(@2x, @3x)選擇正確的圖檔。
2. 網路圖片:使用物件形式,且必須包含 uri。
<Image source={{ uri: 'https://example.com/photo.jpg' }} />
注意! 對於網路圖片,你必須手動設定寬度(width)和高度(height)。與瀏覽器不同,RN 不會等到網路圖片下載完後自動幫你推開版面,如果你沒設定尺寸,圖片的寬高預設是 0,導致畫面上一片空白。
resizeMode:圖片的裁剪哲學
在手機上,圖片的比例經常與容器不符。resizeMode 是你最重要的工具:
cover(預設):保持比例放大,填滿容器,可能會裁掉邊邊(類似 CSS 的background-size: cover)。contain:保持比例縮小,確保整張圖都看得見(類似background-size: contain)。stretch:不顧比例,直接拉伸填滿。
ScrollView:手動開啟的滾動世界
這是另一個讓 Web 開發者感到「不直覺」的地方。在瀏覽器中,如果內容超過了 <div> 的高度,只要加上 overflow: scroll(或靠瀏覽器預設),畫面就可以捲動。
在原生手機開發中,滾動不是免費的。
原生 View 預設不可滾動
一個 <View> 就算內容長度超過了螢幕,它也只會死死地待在原地,多出來的內容會被直接截斷。如果你希望畫面可以捲動,你必須明確地使用 <ScrollView>。
效能陷阱:為什麼不能永遠用 ScrollView?
ScrollView 的運作原理很簡單:它會一次性地渲染所有子元件。
如果你只有 5 個卡片,這沒問題。但如果你正在開發一個媒體 APP,要顯示 500 則新聞,ScrollView 會嘗試一次把 500 則內容全部畫出來,這會導致你的 JS 線程被塞爆,APP 瞬間卡死。
小伏筆:為了處理大量數據的列表,我們會使用更高級的
FlatList或SectionList,它們具備「回收機制」,只渲染畫面看得到的項目。這我們會在 Topic 7 效能篇深入討論。
TextInput:與使用者的對話橋樑
處理文字輸入在手機上比在 Web 上複雜得多,因為你需要考慮到那塊突然彈出來、佔據半個螢幕的虛擬鍵盤。
受控元件(Controlled Component)
雖然 TextInput 支援非受控模式,但在 React Native 中,我們強烈建議使用受控模式:
const [text, setText] = useState('');
<TextInput
value={text}
onChangeText={(newText) => setText(newText)} // 注意:是 onChangeText,不是 onChange
placeholder="請輸入內容"
/>
onChangeText 直接回傳字串,這比 Web 的 e.target.value 簡潔許多。
鍵盤類型與跨平台細節
手機開發中最注重「上下文」。如果使用者在輸入電話號碼,你卻彈出全文字鍵盤,那是很糟糕的體驗。
keyboardType:可以設定為numeric(數字)、email-address(郵件) 或phone-pad(撥號盤)。secureTextEntry:設為true即可變成密碼輸入框。
此外,Android 和 iOS 對於 TextInput 的底線樣式、內距(padding)有非常不同的預設值,這也是為什麼你的 APP 在兩個平台上看起來總是有微小差異的原因之一。
總結:從元件看透 RN 的設計哲學
我們來回顧這五大核心元件與 Web 的對照:
| React Native 元件 | HTML 標籤 (對標) | 關鍵備忘錄 |
|---|---|---|
| View | <div> | 容器基礎,預設 flexDirection: column |
| Text | <span> / <p> | 所有文字必備,RN 唯一支援樣式繼承的地方 |
| Image | <img> | 網路圖片必須手動給寬高,注意 resizeMode |
| ScrollView | overflow: scroll | 必須顯式包裹,內容多時有效能風險 |
| TextInput | <input> | 使用 onChangeText,注意鍵盤類型優化 |
這些元件就是透過我們上一課學到的 JSI 或 Bridge 告訴原生層:「請幫我畫一個長這樣的 UI」。
你可能已經發現,光有這些元件是不夠的。在開發過程中,你需要一個環境來運行這些指令,需要工具來打包圖片,甚至需要一個「預覽器」來即時看到效果。這就是為什麼我們需要理解 Expo 與 React Native CLI。
這些元件雖然在 JS 層寫起來大同小異,但在不同工具鏈的封裝下,它們的載入速度、打包方式甚至功能邊界都會有所不同。在下一節,我們將探討這兩大工具鏈的本質差異,並看看你目前的 APP 究竟是站在哪一個陣營。
承前啟後:從「畫什麼」到「怎麼跑」
現在你已經理解了 RN 的「磚塊」(元件)是什麼,以及它們為什麼不能直接用 HTML 代替。這解決了「畫什麼」的問題。
但身為一個專業的開發者,你還需要知道這些磚塊是如何被搬到手機上的。你目前的 APP 是用 Expo 寫的嗎?還是純 React Native CLI?如果你想加入一個 Expo 目前不支援的原生套件,該怎麼辦?下一節,我們將拆解 Expo vs React Native CLI 的權衡與選擇。