React Native 標籤檢視
React Native 標籤檢視是一個跨平台標籤檢視元件,適用於使用 react-native-pager-view
在 Android 和 iOS 上及 PanResponder 在 Web、macOS 和 Windows 上,實作的 React Native。
它預設遵循材質設計指南,但你也可以使用自己的自訂標籤列或將標籤列放置在底部。

此套件未整合 React 導航。如果你想將標籤檢視與 React 導航的導航系統整合,例如:在標籤列中顯示畫面,並能夠使用 navigation.navigate
等方式在它們之間導航,請改用 材質頂端 tab 導航器。
安裝
若要使用此套件,請在專案根目錄中開啟終端機,並執行
- npm
- Yarn
- pnpm
npm install react-native-tab-view
yarn add react-native-tab-view
pnpm add react-native-tab-view
接下來,如果你打算支援 iOS 和 Android,請安裝 react-native-pager-view
。
如果你使用的是 Expo,請執行以下操作,以確保取得相容的函式庫版本
expo install react-native-pager-view
如果你未在使用 Expo,請執行下列操作
- npm
- Yarn
- pnpm
npm install react-native-pager-view
yarn add react-native-pager-view
pnpm add react-native-pager-view
完成了!現在你可以在裝置/模擬器上建構和執行應用程式。
快速入門
import * as React from 'react';
import { View, useWindowDimensions } from 'react-native';
import { TabView, SceneMap } from 'react-native-tab-view';
const FirstRoute = () => (
<View style={{ flex: 1, backgroundColor: '#ff4081' }} />
);
const SecondRoute = () => (
<View style={{ flex: 1, backgroundColor: '#673ab7' }} />
);
const renderScene = SceneMap({
first: FirstRoute,
second: SecondRoute,
});
export default function TabViewExample() {
const layout = useWindowDimensions();
const [index, setIndex] = React.useState(0);
const [routes] = React.useState([
{ key: 'first', title: 'First' },
{ key: 'second', title: 'Second' },
]);
return (
<TabView
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={setIndex}
initialLayout={{ width: layout.width }}
/>
);
}
在 Snack 上的更多範例
API 參考
此套件中有一個 TabView
元件,可讓您用於呈現標籤檢視,以及一個 TabBar
元件,是預設的標籤列實作。
TabView
負責呈現和管理標籤的容器元件。預設遵循 Material Design 的樣式。
基本用法的範例如下
<TabView
navigationState={{ index, routes }}
onIndexChange={setIndex}
renderScene={SceneMap({
first: FirstRoute,
second: SecondRoute,
})}
/>
TabView 屬性
navigationState
(必填
)
標籤檢視的狀態。狀態應包含下列屬性
index
:代表routes
陣列中 active 路由索引的數字routes
:包含路徑物件清單的陣列,可用於呈現標籤
各個路徑物件應包含下列屬性
key
:用於識別路由的唯一金鑰 (必填)title
:在標籤列中顯示的路徑標題icon
:在標籤列中顯示的路徑圖示accessibilityLabel
:標籤按鈕的輔助技術標籤testID
:標籤按鈕的測試識別碼
範例
{
index: 1,
routes: [
{ key: 'music', title: 'Music' },
{ key: 'albums', title: 'Albums' },
{ key: 'recents', title: 'Recents' },
{ key: 'purchased', title: 'Purchased' },
]
}
TabView
是個受控元件,也就是說 index
需透過 onIndexChange
回呼更新。
onIndexChange
(必填
)
標籤變更時會呼叫的回呼,並接收新標籤索引作為引數。呼叫後必須更新瀏覽狀態,否則會中斷變更。
renderScene
(必填
)
回呼,傳回一個 react 元素,作為標籤的頁面呈現。接收一個物件,其中包含作為引數的路徑
const renderScene = ({ route, jumpTo }) => {
switch (route.key) {
case 'music':
return <MusicRoute jumpTo={jumpTo} />;
case 'albums':
return <AlbumsRoute jumpTo={jumpTo} />;
}
};
您需確保個別路由實作 shouldComponentUpdate
才能提升效能。您可以使用 SceneMap
輔助函式輕鬆指定元件。
SceneMap
會將物件取用 route.key
對應的 React 元件,並傳回一個函式以搭配 renderScene
屬性使用。
import { SceneMap } from 'react-native-tab-view';
...
const renderScene = SceneMap({
music: MusicRoute,
albums: AlbumsRoute,
});
這個方式可以更輕鬆地指定元件,並負責實作 shouldComponentUpdate
方法。
各場景接收下列屬性
route
:元件呈現的目前路由jumpTo
:跳至其他標籤的方法,並將route.key
作為引數position
:表示目前位置的動畫節點
jumpTo
方法可程式化地導覽至其他分頁
props.jumpTo('albums');
以 SceneMap
呈現的所有場景都使用 React.PureComponent
來最佳化,且在父項的道具或狀態變更時不會重新產生。如果你想進一步控制場景的更新方式(例如:就算 navigationState
沒有變更也要觸發重新產生),請直接使用 renderScene
,而不要透過 SceneMap
。
重要提示:請勿傳遞內嵌函式給 SceneMap
,例如請不要執行下列操作
SceneMap({
first: () => <FirstRoute foo={props.foo} />,
second: SecondRoute,
});
請始終在檔案的頂層定義你的元件。如果你傳遞內嵌函式,則會在每次產生時重新建立元件,這將導致整個路由在每次變更時都解除安裝接著重新安裝。這對效能很不好,同時也會造成任何區域狀態遺失。
如果你需要傳遞其他道具,請使用自訂的 renderScene
函式
const renderScene = ({ route }) => {
switch (route.key) {
case 'first':
return <FirstRoute foo={this.props.foo} />;
case 'second':
return <SecondRoute />;
default:
return null;
}
};
renderTabBar
傳回要作為分頁列使用的自訂 React 元素的呼叫回函
import { TabBar } from 'react-native-tab-view';
...
<TabView
renderTabBar={props => <TabBar {...props} />}
...
/>
如果這裡未指定,則會產生預設的頁籤列。你可以傳遞這些道具來自訂預設的頁籤列、提供你自己的頁籤列,或徹底停用頁籤列。
<TabView
renderTabBar={() => null}
...
/>
tabBarPosition
在分頁檢視中的分頁列位置。可能的值是 'top'
和 'bottom'
。預設值為 'top'
。
lazy
取得包含目前路由的物件,並傳回布林值以指出是否延遲產生場景的函式。
預設會產生所有場景,以提供更流暢的滑動體驗。不過你可能會想延後處理未聚焦的場景,直到使用者看到它們為止。如要為特定場景啟用延遲產生,請針對該 route
的 getLazy
傳回 true
<TabView
lazy={({ route }) => route.name === 'Albums'}
...
/>
當你為畫面啟用延遲產生時,當它變成焦點時通常需要一些時間才能產生。你可以使用 renderLazyPlaceholder
道具自訂使用者在這段短時間內看到什麼。
你也可以傳遞一個布林值來為所有場景啟用延遲產生
<TabView lazy />
lazyPreloadDistance
當啟用 lazy
時,你可以藉由這個道具指定預載多少相鄰的路由。這個值的預設值為 0
,表示延遲載入的頁面會在它們進入視窗時載入。
renderLazyPlaceholder
呼叫會傳回自訂 React 元素,以產生尚未呈現的路由。會收到包含路由的物件作為引數。還需要啟用 [`lazy`](/docs/lazy-loading-routes) 屬性。
此畫面通常只顯示一小段時間。請維持小巧。
預設會產生 `null`。
keyboardDismissMode
字串,表示是否在拖曳手勢下關閉鍵盤。可能的數值為
'auto'
(預設值):在索引變更時關閉鍵盤。'on-drag'
:在拖曳開始時關閉鍵盤。'none'
:拖曳不關閉鍵盤。
swipeEnabled
布林值,表示是否啟用滑動手勢。滑動手勢預設為啟用。傳遞 `false` 會停用滑動手勢,但使用者仍可透過按下分頁欄切換分頁。
animationEnabled
在變更分頁時啟用動畫。預設為 true。
onSwipeStart
在滑動手勢開始時呼叫的呼叫回,即使用者觸碰並移動過螢幕時。
onSwipeEnd
在滑動手勢結束時呼叫的呼叫回,即在使用者於滑動手勢後放開螢幕時。
initialLayout
包含各個畫面的初始高度及寬度的物件。傳遞此物件會改善初始呈現效能。對於大多數 app 來說,這是個不錯的預設值。
<TabView
initialLayout={{ width: Dimensions.get('window').width }}
...
/>
overScrollMode
用於覆寫分頁器的 overScroll 模式預設值。可為 `auto`、`always` 或 `never` (僅限 Android)。
sceneContainerStyle
套用到包裝每個畫面的檢視的樣式。可以傳遞此樣式來覆寫一些預設樣式,例如溢位裁剪
pagerStyle
套用到包裝所有場景的分頁器檢視的樣式。
style
套用至分頁檢視容器的樣式。
TabBar
遵循素材設計的主題標籤欄。若要自訂標籤欄,您需要使用 TabView
的 renderTabBar
屬性來渲染 TabBar
並傳遞額外的屬性。
例如,若要自訂指示器顏色和標籤欄背景顏色,您可以分別傳遞 indicatorStyle
和 style
屬性給 TabBar
const renderTabBar = props => (
<TabBar
{...props}
indicatorStyle={{ backgroundColor: 'white' }}
style={{ backgroundColor: 'pink' }}
/>
);
//...
return (
<TabView
renderTabBar={renderTabBar}
...
/>
);
TabBar 屬性
getLabelText
傳遞具有目前路由的物件的函數,並傳回標籤的標籤文字。預設使用 route.title
。
<TabBar
getLabelText={({ route }) => route.title}
...
/>
getAccessible
傳遞具有目前路由的物件的函數,並傳回一個布林值以指示是否將標籤標示為 可存取
。預設為 true
。
getAccessibilityLabel
傳遞具有目前路由的物件的函數,並傳回標籤按鈕的可存取性標籤。預設使用 route.accessibilityLabel
(如果已指定),否則使用路由標題。
<TabBar
getAccessibilityLabel={({ route }) => route.accessibilityLabel}
...
/>
getTestID
傳遞具有目前路由的物件的函數,並傳回標籤按鈕的測試 ID,以在各項測試中找到此標籤按鈕。預設使用 route.testID
。
<TabBar
getTestID={({ route }) => route.testID}
...
/>
renderIcon
傳遞具有目前路由、焦點狀態和顏色的物件的函數,並傳回作為圖示要使用的自訂 React 元素。
<TabBar
renderIcon={({ route, focused, color }) => (
<Icon
name={focused ? 'albums' : 'albums-outlined'}
color={color}
/>
)}
...
/>
renderLabel
傳遞具有目前路由、焦點狀態和顏色的物件的函數,並傳回作為標籤要使用的自訂 React 元素。
<TabBar
renderLabel={({ route, focused, color }) => (
<Text style={{ color, margin: 8 }}>
{route.title}
</Text>
)}
...
/>
renderTabBarItem
傳遞 TabBarItemProps
物件的函數,並傳回作為標籤按鈕要使用的自訂 React 元素。
renderIndicator
傳遞具有目前路由的物件的函數,並傳回作為標籤指示器要使用的自訂 React 元素。
renderBadge
傳遞具有目前路由的物件的函數,並傳回作為徽章要使用的自訂 React 元素。
onTabPress
在標籤按下時執行的函數。它接收按下之標籤的場景,可用於執行類似捲動至頂端的動作。
預設情況下,按下標籤也會切換標籤。若要避免這個行為,可以呼叫 preventDefault
<TabBar
onTabPress={({ route, preventDefault }) => {
if (route.key === 'home') {
preventDefault();
// Do something else
}
}}
...
/>
onTabLongPress
在標籤長時間按下時執行的函數,可作為顯示包含更多選項之功能表等用途
activeColor
在作用中標籤中圖示與標籤的客製化顏色。
inactiveColor
在非作用中標籤中圖示與標籤的客製化顏色。
pressColor
材質漣漪的顏色 (僅針對 Android >= 5.0)。
pressOpacity
按下的標籤的不透明度 (僅針對 iOS 和 Android < 5.0)。
scrollEnabled
布林值,指示是否要讓標籤列可捲動。
如果您設定 scrollEnabled
為 true
,您也應該在 tabStyle
中指定 width
,以提升初始呈現。
bounces
布林值,指示捲動時標籤列是否會反彈。
tabStyle
套用至標籤列中個別標籤項目之樣式。
預設情況下,所有標籤項目會使用相同、根據容器寬度事先計算的寬度。如果您希望讓它們使用原始寬度,您可以在 tabStyle
中指定 width: 'auto'
。
indicatorStyle
套用至作用中指示器的樣式。
indicatorContainerStyle
套用至指示器之容器檢視的樣式。
labelStyle
套用至標籤項目標籤的樣式。
contentContainerStyle
套用至標籤內部容器的樣式。
樣式
(TabBar
)
套用至分頁工具列容器的樣式。
gap
定義分頁間隔。
testID
分頁工具列的測試 ID。可於測試中用於捲動分頁工具列
最佳化秘訣
避免不必要的再繪製
renderScene
函數會在每次索引變更時呼叫。如果您的 renderScene
函數負擔很重,務必將每個路由移至個別組件中(前提是它們不依賴於索引),並在路由組件中使用 shouldComponentUpdate
或 React.memo
以避免不必要的再繪製。
例如,取代
const renderScene = ({ route }) => {
switch (route.key) {
case 'home':
return (
<View style={styles.page}>
<Avatar />
<NewsFeed />
</View>
);
default:
return null;
}
};
採用下列做法
const renderScene = ({ route }) => {
switch (route.key) {
case 'home':
return <HomeComponent />;
default:
return null;
}
};
其中 <HomeComponent />
是一份 PureComponent
(若您使用的是類別組件)
export default class HomeComponent extends React.PureComponent {
render() {
return (
<View style={styles.page}>
<Avatar />
<NewsFeed />
</View>
);
}
}
或者是,封裝在 React.memo
中(若您使用的是函式組件)
function HomeComponent() {
return (
<View style={styles.page}>
<Avatar />
<NewsFeed />
</View>
);
}
export default React.memo(HomeComponent);
避免單禎延遲
我們需要測量容器的寬度,因此在畫面中彩現某些元素前需要等待。如果您事先知道最初的寬度,您可以將其傳入,那我們就不需要等待測量了。在大部分情況下,它僅是視窗寬度。
例如,傳遞下列 initialLayout
至 TabView
const initialLayout = {
height: 0,
width: Dimensions.get('window').width,
};
分頁檢視仍會因應大小變更做出反應,並適當地調整以容納方向變更等情況。
最佳化大量路由
如果您有大量的路由,特別是影像,這可能會顯著減緩動畫速度。您可以改為僅繪製有限數量的路由。
例如,採用下列做法僅在每側繪製 2 個路由
const renderScene = ({ route }) => {
if (Math.abs(index - routes.indexOf(route)) > 2) {
return <View />;
}
return <MySceneComponent route={route} />;
};
避免在 ScrollView 內部繪製 TabView
在垂直 ScrollView
內部嵌套 TabView
會停用 TabView
內部所繪製的 FlatList
組件中的最佳化。因此,請盡可能避免這類做法。
使用 lazy
和 renderLazyPlaceholder
屬性依需要繪製路由
lazy
選項在預設情況下停用,以提供更順暢的標籤切換體驗,但你可以啟用它,並提供一個佔位元件以獲得更好的延遲載入體驗。啟用 lazy
可以透過在路徑顯示後才渲染路徑,來改善初始載入效能。有關更多詳細資訊,請參閱 prop 參考。