跳到主要内容
版本:7.x

React Native Tab View

React Native Tab View 是一个跨平台的 Tab View 组件,用于 React Native,它在 Android 和 iOS 上使用 react-native-pager-view 实现,在 Web、macOS 和 Windows 上使用 PanResponder 实现。

默认情况下,它遵循 Material Design 指南,但您也可以使用自己的自定义标签栏或将标签栏放置在底部。

此包不与 React Navigation 集成。如果您想将标签视图与 React Navigation 的导航系统集成,例如,希望在标签栏中显示屏幕,并能够使用 navigation.navigate 等在它们之间导航,请使用 Material Top Tab Navigator 代替。

安装

要使用此包,请在项目根目录中打开终端并运行

npm install react-native-tab-view

接下来,如果您计划支持 iOS 和 Android,请安装 react-native-pager-view

如果您正在使用 Expo,为了确保您获得兼容版本的库,请运行

expo install react-native-pager-view

如果您没有使用 Expo,请运行以下命令

npm install react-native-pager-view

完成了!现在您可以在您的设备/模拟器上构建并运行该应用了。

快速开始

import * as React from 'react';
import { View, useWindowDimensions } from 'react-native';
import { TabView, SceneMap } from 'react-native-tab-view';

const renderScene = SceneMap({
first: FirstRoute,
second: SecondRoute,
});

const routes = [
{ key: 'first', title: 'First' },
{ key: 'second', title: 'Second' },
];

export default function TabViewExample() {
const layout = useWindowDimensions();
const [index, setIndex] = React.useState(0);

return (
<TabView
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={setIndex}
initialLayout={{ width: layout.width }}
/>
);
}
Snack 上尝试

Snack 上的更多示例

API 参考

该包导出一个 TabView 组件,您可以使用它来渲染标签视图,以及一个 TabBar 组件,它是默认的标签栏实现。

TabView

负责渲染和管理标签的容器组件。默认情况下遵循 Material Design 样式。

基本用法如下所示

<TabView
navigationState={{ index, routes }}
onIndexChange={setIndex}
renderScene={SceneMap({
first: FirstRoute,
second: SecondRoute,
})}
/>

TabView Props

标签视图的状态。该状态应包含以下属性

  • 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 是一个受控组件,这意味着 index 需要通过 onIndexChange 回调进行更新。

onIndexChange (required)

在标签更改时调用的回调,接收新标签的索引作为参数。当调用它时,需要更新导航状态,否则更改将被丢弃。

renderScene (required)

回调,返回一个 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 prop 一起使用。

import { SceneMap } from 'react-native-tab-view';

...

const renderScene = SceneMap({
music: MusicRoute,
albums: AlbumsRoute,
});

以这种方式指定组件更容易,并且负责实现 shouldComponentUpdate 方法。

每个场景接收以下 props

  • 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

函数,接受一个包含当前路由的对象,并返回一个布尔值,指示是否延迟渲染场景。

默认情况下,所有场景都将被渲染以提供更流畅的滑动体验。但是您可能希望推迟渲染未聚焦的场景,直到用户看到它们为止。要为特定场景启用延迟渲染,请从该 routelazy 返回 true

<TabView
lazy={({ route }) => route.name === 'Albums'}
...
/>

当您为一个屏幕启用延迟渲染时,通常需要一些时间才能在它进入焦点时渲染。您可以使用 renderLazyPlaceholder prop 来自定义用户在此短暂期间看到的内容。

您还可以传递一个布尔值来为所有场景启用延迟加载

<TabView lazy />
lazyPreloadDistance

当启用 lazy 时,您可以指定应预加载多少个相邻路由,通过此 prop。此值默认为 0,这意味着延迟加载的页面会在它们进入视口时加载。

renderLazyPlaceholder

回调,返回一个自定义 React 元素,用于渲染尚未渲染的路由。接收一个包含路由作为参数的对象。还需要启用 lazy prop。

此视图通常仅显示一瞬间。保持轻量。

默认情况下,这将渲染 null

keyboardDismissMode

字符串,指示是否在响应拖动手势时关闭键盘。可能的值为

  • 'auto'(默认):当索引更改时,键盘将被关闭。
  • 'on-drag':当拖动开始时,键盘将被关闭。
  • 'none':拖动不会关闭键盘。
swipeEnabled

布尔值,指示是否启用滑动 gestures。默认情况下启用滑动 gestures。传递 false 将禁用滑动 gestures,但用户仍然可以通过按标签栏来切换标签。

animationEnabled

在更改标签时启用动画。默认情况下为 true。

onSwipeStart

回调,在滑动 gesture 开始时调用,即用户触摸屏幕并移动它时。

onSwipeEnd

回调,在滑动 gesture 结束时调用,即用户在滑动 gesture 后将手指从屏幕上抬起时。

initialLayout

包含屏幕初始高度和宽度的对象。传递此项将提高初始渲染性能。对于大多数应用来说,这是一个很好的默认值

<TabView
initialLayout={{ width: Dimensions.get('window').width }}
...
/>
overScrollMode

用于覆盖 pager 的 overScroll 模式的默认值。可以是 autoalwaysnever(仅限 Android)。

pagerStyle

应用于包装所有场景的 pager 视图的样式。

style

应用于标签视图容器的样式。

TabBar

Material Design 主题标签栏。要自定义标签栏,您需要使用 TabViewrenderTabBar prop 来渲染 TabBar 并传递额外的 props。

例如,要自定义指示器颜色和标签栏背景颜色,您可以分别将 indicatorStylestyle props 传递给 TabBar

const renderTabBar = props => (
<TabBar
{...props}
indicatorStyle={{ backgroundColor: 'white' }}
style={{ backgroundColor: 'pink' }}
/>
);

//...


return (
<TabView
renderTabBar={renderTabBar}
...
/>
);

TabBar Props

renderTabBarItem

函数,它接受一个 TabBarItemProps 对象,并返回一个自定义 React 元素,用作标签按钮。

renderIndicator

函数,它接受一个包含当前路由的对象,并返回一个自定义 React 元素,用作标签指示器。

onTabPress

在标签按下时执行的函数。它接收按下标签的场景,对于诸如滚动到顶部之类的事情很有用。

默认情况下,标签按下也会切换标签。要阻止此行为,您可以调用 preventDefault

<TabBar
onTabPress={({ route, preventDefault }) => {
if (route.key === 'home') {
preventDefault();

// Do something else
}
}}
...
/>
onTabLongPress

在标签长按时执行的函数,用于诸如显示具有更多选项的菜单之类的事情

activeColor

活动标签中图标和标签的自定义颜色。

inactiveColor

非活动标签中图标和标签的自定义颜色。

pressColor

Material ripple 的颜色(仅限 Android >= 5.0)。

pressOpacity

按下标签的透明度(仅限 iOS 和 Android < 5.0)。

scrollEnabled

布尔值,指示是否使标签栏可滚动。

如果您将 scrollEnabled 设置为 true,您还应该在 tabStyle 中指定 width 以改善初始渲染。

bounces

布尔值,指示标签栏在滚动时是否反弹。

tabStyle

应用于标签栏中各个标签项的样式。

默认情况下,所有标签项都占用基于容器宽度预先计算的相同宽度。如果您希望它们采用原始宽度,您可以在 tabStyle 中指定 width: 'auto'

indicatorStyle

应用于活动指示器的样式。

indicatorContainerStyle

应用于指示器的容器视图的样式。

contentContainerStyle

应用于标签内部容器的样式。

style (TabBar)

应用于标签栏容器的样式。

gap

标签项之间的间距。

testID (TabBar)

标签栏的测试 ID。可用于在测试中滚动标签栏

选项

选项描述了应如何配置每个标签。有两种指定选项的方式

  • commonOptions:应用于所有标签的选项。
  • options:应用于特定标签的选项。它以路由键作为键,并以包含选项的对象作为值。

示例

<TabView
commonOptions={{
icon: ({ route, focused, color }) => (
<Icon name={route.icon} color={color} />
),
}}
options={{
albums: {
labelText: 'Albums',
},
profile: {
labelText: 'Profile',
},
}}
/>

以下选项可用

accessibilityLabel

标签按钮的辅助功能标签。如果指定了 route.accessibilityLabel,则默认使用它,否则使用路由标题。

accessible

是否将标签标记为 accessible。默认为 true

testID

标签按钮的测试 ID。默认情况下使用 route.testID

labelText

标签按钮的标签文本。默认情况下使用 route.title

labelAllowFontScaling

标签字体是否应缩放以遵循“文本大小”辅助功能设置。默认为 true

href

用于 Web 上标签按钮的锚标记的 URL。

label

一个函数,返回一个自定义 React 元素以用作标签。该函数接收一个具有以下属性的对象

  • route - 标签的路由对象。
  • labelText - 在 labelText 选项或 route title 中指定的标签文本。
  • focused - 标签是否处于聚焦状态。
  • color - 标签的颜色。
  • allowFontScaling - 标签字体是否应缩放以遵循“文本大小”辅助功能设置。
  • style - 标签的样式对象。
label: ({ route, labelText, focused, color }) => (
<Text style={{ color, margin: 8 }}>{labelText ?? route.name}</Text>
);
labelStyle

应用于标签项标签的样式。

icon

一个函数,返回一个自定义 React 元素以用作图标。该函数接收一个具有以下属性的对象

  • route - 标签的路由对象。
  • focused - 图标是否处于聚焦状态。
  • color - 图标的颜色。
  • size - 图标的大小。
icon: ({ route, focused, color }) => (
<Icon name={focused ? 'albums' : 'albums-outlined'} color={color} />
);
badge

一个函数,返回一个自定义 React 元素以用作徽章。该函数接收一个具有以下属性的对象

  • route - 标签的路由对象。
badge: ({ route }) => (
<View
style={{ backgroundColor: 'red', width: 20, height: 20, borderRadius: 10 }}
/>
);
sceneStyle

应用于包装每个屏幕的视图的样式。您可以传递此项以覆盖某些默认样式,例如溢出裁剪。

优化技巧

避免不必要的重新渲染

每次索引更改时都会调用 renderScene 函数。如果您的 renderScene 函数开销很大,则最好将每个路由移动到单独的组件,如果它们不依赖于索引,并在您的路由组件中使用 shouldComponentUpdateReact.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 组件中的优化。因此,如果可能,请避免这样做。

使用 lazyrenderLazyPlaceholder props 按需渲染路由

默认情况下禁用 lazy 选项以提供更流畅的标签切换体验,但您可以启用它并为占位符组件提供更好的延迟加载体验。启用 lazy 可以通过仅在路由进入视图时渲染它们来提高初始加载性能。有关更多详细信息,请参阅 prop 参考