React Native 选项卡视图
React Native 选项卡视图是一个跨平台的选项卡视图组件,用于 React Native,它使用 react-native-pager-view
在 Android 和 iOS 上实现,并使用 PanResponder 在 Web、macOS 和 Windows 上实现。
它默认遵循 Material Design 指南,但您也可以使用自己的自定义选项卡栏或将选项卡栏放置在底部。
此包不与 React Navigation 集成。如果您想将选项卡视图与 React Navigation 的导航系统集成,例如,想要在选项卡栏中显示屏幕并能够使用 navigation.navigate
等在它们之间导航,请使用 Material Top Tab Navigator 代替。
安装
要使用此包,请在项目根目录中打开一个终端并运行
- 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
数组中活动路由索引的数字routes
: 包含用于渲染标签的路由对象列表的数组
每个路由对象应包含以下属性
key
: 用于标识路由的唯一键(必填)title
: 在标签栏中显示的路由标题icon
: 在标签栏中显示的路由图标accessibilityLabel
: 标签按钮的可访问性标签testID
: 标签按钮的测试 ID
示例
{
index: 1,
routes: [
{ key: 'music', title: 'Music' },
{ key: 'albums', title: 'Albums' },
{ key: 'recents', title: 'Recents' },
{ key: 'purchased', title: 'Purchased' },
]
}
TabView
是一个受控组件,这意味着需要通过 onIndexChange
回调更新 index
。
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
进行优化,并且在父级组件的 props 或状态发生变化时不会重新渲染。如果您需要更细粒度地控制场景的更新方式(例如,即使 navigationState
未发生变化也要触发重新渲染),请直接使用 renderScene
而不是使用 SceneMap
。
重要: 不要 将内联函数传递给 SceneMap
,例如,不要执行以下操作
SceneMap({
first: () => <FirstRoute foo={props.foo} />,
second: SecondRoute,
});
始终在文件顶层定义您的组件。如果您传递内联函数,它将在每次渲染时重新创建组件,这会导致整个路由在每次更改时卸载和重新挂载。这会严重影响性能,还会导致任何局部状态丢失。
如果您需要传递其他 props,请使用自定义 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} />}
...
/>
如果未指定,则会渲染默认的选项卡栏。您可以传递这些 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` 属性也需要启用。
此视图通常只显示几秒钟。保持轻量级。
默认情况下,这将渲染 `null`。
keyboardDismissMode
字符串,指示键盘是否会响应拖动手势而消失。可能的取值为
'auto'
(默认):当索引更改时,键盘会消失。'on-drag'
:当拖动开始时,键盘会消失。'none'
:拖动不会使键盘消失。
swipeEnabled
布尔值,指示是否启用滑动手势。默认情况下启用滑动手势。传递 `false` 将禁用滑动手势,但用户仍然可以通过按下标签栏来切换标签。
animationEnabled
在更改标签时启用动画。默认情况下为 true。
onSwipeStart
回调函数,在滑动手势开始时调用,即用户触摸屏幕并移动它。
onSwipeEnd
回调函数,在滑动手势结束时调用,即用户在滑动手势后从屏幕上抬起手指。
initialLayout
包含屏幕初始高度和宽度的对象。传递此对象将提高初始渲染性能。对于大多数应用程序,这是一个很好的默认值。
<TabView
initialLayout={{ width: Dimensions.get('window').width }}
...
/>
overScrollMode
用于覆盖分页器 overScroll 模式默认值。可以是 auto
、always
或 never
(仅限 Android)。
sceneContainerStyle
应用于包装每个屏幕的视图的样式。您可以传递此样式以覆盖一些默认样式,例如溢出裁剪。
pagerStyle
应用于包装所有场景的分页器视图的样式。
style
应用于选项卡视图容器的样式。
TabBar
Material Design 主题选项卡栏。要自定义选项卡栏,您需要使用 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
一个函数,它接收一个包含当前路由的对象,并返回一个布尔值,以指示是否将选项卡标记为 accessible
。默认值为 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
要应用于选项卡内部容器的样式。
style
(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
将 TabView
嵌套在垂直 ScrollView
中会禁用 TabView
内部渲染的 FlatList
组件中的优化。因此,如果可能,请避免这样做。
使用 lazy
和 renderLazyPlaceholder
属性按需渲染路由
lazy
选项默认情况下处于禁用状态,以提供更流畅的选项卡切换体验,但你可以启用它并提供一个占位符组件以获得更好的延迟加载体验。启用 lazy
可以通过仅在路由进入视图时渲染它们来提高初始加载性能。有关更多详细信息,请参阅 属性参考。