Skip to main content
Version: 6.x

React Native 选项卡视图

React Native Tab View 是 React Native 的跨平台选项卡视图组件,在 Android 和 iOS 上使用 react-native-pager-view 实现,在 Web、macOS 和 Windows 上使用 PanResponder 实现。

¥React Native Tab View is a cross-platform Tab View component for React Native implemented using react-native-pager-view on Android & iOS, and PanResponder on Web, macOS, and Windows.

默认情况下,它遵循材料设计指南,但你也可以使用自己的自定义选项卡栏或将选项卡栏放置在底部。

¥It follows material design guidelines by default, but you can also use your own custom tab bar or position the tab bar at the bottom.

React Native Tab View Demo

该包不与 React Navigation 集成。如果你想将选项卡视图与 React Navigation 的导航系统集成,例如想要在选项卡栏中显示屏幕并能够使用 navigation.navigate 等在它们之间导航,请改用 材质顶部选项卡导航器

¥This package doesn't integrate with React Navigation. If you want to integrate the tab view with React Navigation's navigation system, e.g. want to show screens in the tab bar and be able to navigate between them using navigation.navigate etc, use Material Top Tab Navigator instead.

安装

¥Installation

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

¥To use this package, open a Terminal in the project root and run:

npm install react-native-tab-view

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

¥Next, install react-native-pager-view if you plan to support iOS and Android.

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

¥If you are using Expo, to ensure that you get the compatible versions of the libraries, run:

expo install react-native-pager-view

如果你不使用 Expo,请运行以下命令:

¥If you are not using Expo, run the following:

npm install react-native-pager-view

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

¥We're done! Now you can build and run the app on your device/simulator.

快速开始

¥Quick start

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 上试试这个例子

¥Try this example on Snack

有关小样的更多示例

¥More examples on Snack

API 参考

¥API reference

该包导出一个 TabView 组件(用于呈现选项卡视图)和一个 TabBar 组件(默认选项卡栏实现)。

¥The package exports a TabView component which is the one you'd use to render the tab view, and a TabBar component which is the default tab bar implementation.

TabView

负责呈现和管理选项卡的容器组件。默认遵循材料设计风格。

¥Container component responsible for rendering and managing tabs. Follows material design styles by default.

基本用法如下:

¥Basic usage look like this:

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

TabView 属性

¥TabView Props

选项卡视图的状态。状态应包含以下属性:

¥State for the tab view. The state should contain the following properties:

  • index:代表 routes 数组中活动路由索引的数字

    ¥index: a number representing the index of the active route in the routes array

  • routes:包含用于渲染选项卡的路由对象列表的数组

    ¥routes: an array containing a list of route objects used for rendering the tabs

每个路由对象应包含以下属性:

¥Each route object should contain the following properties:

  • key:识别路由的唯一密钥(必需)

    ¥key: a unique key to identify the route (required)

  • title:要在选项卡栏中显示的路由的标题

    ¥title: title for the route to display in the tab bar

  • icon:标签栏中显示的路由图标

    ¥icon: icon for the route to display in the tab bar

  • accessibilityLabel:选项卡按钮的辅助功能标签

    ¥accessibilityLabel: accessibility label for the tab button

  • testID:测试选项卡按钮的 id

    ¥testID: test id for the tab button

示例:

¥Example:

{
index: 1,
routes: [
{ key: 'music', title: 'Music' },
{ key: 'albums', title: 'Albums' },
{ key: 'recents', title: 'Recents' },
{ key: 'purchased', title: 'Purchased' },
]
}

TabView 是受控组件,这意味着 index 需要通过 onIndexChange 回调进行更新。

¥TabView is a controlled component, which means the index needs to be updated via the onIndexChange callback.

onIndexChange(required)

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

¥Callback which is called on tab change, receives the index of the new tab as argument. The navigation state needs to be updated when it's called, otherwise the change is dropped.

renderScene(required)

回调返回一个 React 元素以呈现为选项卡的页面。接收包含路由作为参数的对象:

¥Callback which returns a react element to render as the page for the tab. Receives an object containing the route as the argument:

const renderScene = ({ route, jumpTo }) => {
switch (route.key) {
case 'music':
return <MusicRoute jumpTo={jumpTo} />;
case 'albums':
return <AlbumsRoute jumpTo={jumpTo} />;
}
};

你需要确保你的各个路由实现 shouldComponentUpdate 以提高性能。为了更轻松地指定组件,你可以使用 SceneMap 辅助程序。

¥You need to make sure that your individual routes implement a shouldComponentUpdate to improve the performance. To make it easier to specify the components, you can use the SceneMap helper.

SceneMap 接受一个带有 route.key 到 React 组件映射的对象,并返回一个与 renderScene prop 一起使用的函数。

¥SceneMap takes an object with the mapping of route.key to React components and returns a function to use with renderScene prop.

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

...

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

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

¥Specifying the components this way is easier and takes care of implementing a shouldComponentUpdate method.

每个场景都会收到以下属性:

¥Each scene receives the following props:

  • route:组件渲染的当前路由

    ¥route: the current route rendered by the component

  • jumpTo:跳转到其他选项卡的方法,采用 route.key 作为参数

    ¥jumpTo: method to jump to other tabs, takes a route.key as it's argument

  • position:代表当前位置的动画节点

    ¥position: animated node which represents the current position

jumpTo 方法可用于以编程方式导航到其他选项卡:

¥The jumpTo method can be used to navigate to other tabs programmatically:

props.jumpTo('albums');

所有使用 SceneMap 渲染的场景都使用 React.PureComponent 进行优化,并且当父级的属性或状态发生变化时不会重新渲染。如果你需要更多地控制场景更新方式(例如 - 即使 navigationState 没有更改也触发重新渲染),请直接使用 renderScene 而不是使用 SceneMap

¥All the scenes rendered with SceneMap are optimized using React.PureComponent and don't re-render when parent's props or states change. If you need more control over how your scenes update (e.g. - triggering a re-render even if the navigationState didn't change), use renderScene directly instead of using SceneMap.

重要:不要将内联函数传递给 SceneMap,例如,不要执行以下操作:

¥IMPORTANT: Do not pass inline functions to SceneMap, for example, don't do the following:

SceneMap({
first: () => <FirstRoute foo={props.foo} />,
second: SecondRoute,
});

始终在文件顶层的其他位置定义组件。如果你传递内联函数,它将在每次渲染时重新创建组件,这将导致整个路由卸载并重新安装每次更改。这对性能非常不利,并且还会导致任何本地状态丢失。

¥Always define your components elsewhere in the top level of the file. If you pass inline functions, it'll re-create the component every render, which will cause the entire route to unmount and remount every change. It's very bad for performance and will also cause any local state to be lost.

如果你需要传递额外的 props,请使用自定义 renderScene 函数:

¥If you need to pass additional props, use a custom renderScene function:

const renderScene = ({ route }) => {
switch (route.key) {
case 'first':
return <FirstRoute foo={this.props.foo} />;
case 'second':
return <SecondRoute />;
default:
return null;
}
};
renderTabBar

返回自定义 React 元素以用作选项卡栏的回调:

¥Callback which returns a custom React Element to use as the tab bar:

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

...

<TabView
renderTabBar={props => <TabBar {...props} />}
...
/>

如果未指定,则呈现默认选项卡栏。你可以通过此属性来自定义默认选项卡栏、提供你自己的选项卡栏或完全禁用选项卡栏。

¥If this is not specified, the default tab bar is rendered. You pass this props to customize the default tab bar, provide your own tab bar, or disable the tab bar completely.

<TabView
renderTabBar={() => null}
...
/>
tabBarPosition

选项卡视图中选项卡栏的位置。可能的值为 'top''bottom'。默认为 'top'

¥Position of the tab bar in the tab view. Possible values are 'top' and 'bottom'. Defaults to 'top'.

lazy

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

¥Function which takes an object with the current route and returns a boolean to indicate whether to lazily render the scenes.

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

¥By default all scenes are rendered to provide a smoother swipe experience. But you might want to defer the rendering of unfocused scenes until the user sees them. To enable lazy rendering for a particular scene, return true from getLazy for that route:

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

当你为屏幕启用延迟渲染时,当它进入焦点时通常需要一些时间来渲染。你可以使用 renderLazyPlaceholder 属性来自定义用户在这段短时间内看到的内容。

¥When you enable lazy rendering for a screen, it will usually take some time to render when it comes into focus. You can use the renderLazyPlaceholder prop to customize what the user sees during this short period.

你还可以传递一个布尔值来为所有场景启用惰性:

¥You can also pass a boolean to enable lazy for all of the scenes:

<TabView lazy />
lazyPreloadDistance

当启用 lazy 时,你可以指定应使用此属性预加载多少条相邻路由。该值默认为 0,这意味着惰性页面在进入视口时加载。

¥When lazy is enabled, you can specify how many adjacent routes should be preloaded with this prop. This value defaults to 0 which means lazy pages are loaded as they come into the viewport.

renderLazyPlaceholder

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

¥Callback which returns a custom React Element to render for routes that haven't been rendered yet. Receives an object containing the route as the argument. The lazy prop also needs to be enabled.

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

¥This view is usually only shown for a split second. Keep it lightweight.

默认情况下,这会呈现 null

¥By default, this renders null.

keyboardDismissMode

指示键盘是否因拖动手势而消失的字符串。可能的值为:

¥String indicating whether the keyboard gets dismissed in response to a drag gesture. Possible values are:

  • 'auto'(默认):当索引更改时,键盘将消失。

    ¥'auto' (default): the keyboard is dismissed when the index changes.

  • 'on-drag':当拖动开始时键盘将消失。

    ¥'on-drag': the keyboard is dismissed when a drag begins.

  • 'none':拖动不会关闭键盘。

    ¥'none': drags do not dismiss the keyboard.

swipeEnabled

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

¥Boolean indicating whether to enable swipe gestures. Swipe gestures are enabled by default. Passing false will disable swipe gestures, but the user can still switch tabs by pressing the tab bar.

animationEnabled

更改选项卡时启用动画。默认情况下这是真的。

¥Enables animation when changing tab. By default it's true.

onSwipeStart

当滑动手势开始时调用的回调,即用户触摸屏幕并移动它。

¥Callback which is called when the swipe gesture starts, i.e. the user touches the screen and moves it.

onSwipeEnd

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

¥Callback which is called when the swipe gesture ends, i.e. the user lifts their finger from the screen after the swipe gesture.

initialLayout

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

¥Object containing the initial height and width of the screens. Passing this will improve the initial rendering performance. For most apps, this is a good default:

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

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

¥Used to override default value of pager's overScroll mode. Can be auto, always or never (Android only).

sceneContainerStyle

应用于环绕每个屏幕的视图的样式。你可以传递它来覆盖一些默认样式,例如溢出裁剪:

¥Style to apply to the view wrapping each screen. You can pass this to override some default styles such as overflow clipping:

pagerStyle

应用于封装所有场景的寻呼机视图的样式。

¥Style to apply to the pager view wrapping all the scenes.

style

应用于选项卡视图容器的样式。

¥Style to apply to the tab view container.

TabBar

材料设计主题标签栏。要自定义选项卡栏,你需要使用 TabViewrenderTabBar 属性来渲染 TabBar 并传递其他属性。

¥Material design themed tab bar. To customize the tab bar, you'd need to use the renderTabBar prop of TabView to render the TabBar and pass additional props.

例如,要自定义指示器颜色和选项卡栏背景颜色,可以分别将 indicatorStylestyle 属性传递给 TabBar

¥For example, to customize the indicator color and the tab bar background color, you can pass indicatorStyle and style props to the TabBar respectively:

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

//...


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

TabBar 属性

¥TabBar Props

getLabelText

函数接受当前路由的对象并返回选项卡的标签文本。默认使用 route.title

¥Function which takes an object with the current route and returns the label text for the tab. Uses route.title by default.

<TabBar
getLabelText={({ route }) => route.title}
...
/>
getAccessible

该函数接受当前路由的对象并返回一个布尔值以指示是否将选项卡标记为 accessible。默认为 true

¥Function which takes an object with the current route and returns a boolean to indicate whether to mark a tab as accessible. Defaults to true.

getAccessibilityLabel

函数采用当前路径的对象并返回选项卡按钮的辅助功能标签。如果指定,则默认使用 route.accessibilityLabel,否则使用路由标题。

¥Function which takes an object with the current route and returns a accessibility label for the tab button. Uses route.accessibilityLabel by default if specified, otherwise uses the route title.

<TabBar
getAccessibilityLabel={({ route }) => route.accessibilityLabel}
...
/>
getTestID

函数接受当前路由的对象并返回选项卡按钮的测试 ID,以在测试中找到此选项卡按钮。默认使用 route.testID

¥Function which takes an object with the current route and returns a test id for the tab button to locate this tab button in tests. Uses route.testID by default.

<TabBar
getTestID={({ route }) => route.testID}
...
/>
renderIcon

函数接受具有当前路由、焦点状态和颜色的对象,并返回一个用作图标的自定义 React 元素。

¥Function which takes an object with the current route, focused status and color and returns a custom React Element to be used as a icon.

<TabBar
renderIcon={({ route, focused, color }) => (
<Icon
name={focused ? 'albums' : 'albums-outlined'}
color={color}
/>
)}
...
/>
renderLabel

函数接受具有当前路由、聚焦状态和颜色的对象,并返回一个用作标签的自定义 React 元素。

¥Function which takes an object with the current route, focused status and color and returns a custom React Element to be used as a label.

<TabBar
renderLabel={({ route, focused, color }) => (
<Text style={{ color, margin: 8 }}>
{route.title}
</Text>
)}
...
/>
renderTabBarItem

该函数接受 TabBarItemProps 对象并返回用作选项卡按钮的自定义 React 元素。

¥Function which takes a TabBarItemProps object and returns a custom React Element to be used as a tab button.

renderIndicator

函数接受当前路由的对象并返回一个自定义的 React 元素以用作选项卡指示器。

¥Function which takes an object with the current route and returns a custom React Element to be used as a tab indicator.

renderBadge

函数接受当前路由的对象并返回一个自定义的 React 元素以用作徽章。

¥Function which takes an object with the current route and returns a custom React Element to be used as a badge.

onTabPress

按下 Tab 时执行的函数。它接收按下的选项卡的场景,对于滚动到顶部之类的事情很有用。

¥Function to execute on tab press. It receives the scene for the pressed tab, useful for things like scroll to top.

默认情况下,按 Tab 键也会切换选项卡。为了防止这种行为,你可以调用 preventDefault

¥By default, tab press also switches the tab. To prevent this behavior, you can call preventDefault:

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

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

长按选项卡时执行的功能,用于显示包含更多选项的菜单等

¥Function to execute on tab long press, use for things like showing a menu with more options

activeColor

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

¥Custom color for icon and label in the active tab.

inactiveColor

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

¥Custom color for icon and label in the inactive tab.

pressColor

材质波纹的颜色(仅限 Android >= 5.0)。

¥Color for material ripple (Android >= 5.0 only).

pressOpacity

按下的选项卡的不透明度(仅限 iOS 和 Android < 5.0)。

¥Opacity for pressed tab (iOS and Android < 5.0 only).

scrollEnabled

布尔值,指示是否使选项卡栏可滚动。

¥Boolean indicating whether to make the tab bar scrollable.

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

¥If you set scrollEnabled to true, you should also specify a width in tabStyle to improve the initial render.

bounces

布尔值,指示滚动时选项卡栏是否弹起。

¥Boolean indicating whether the tab bar bounces when scrolling.

tabStyle

应用于选项卡栏中各个选项卡项目的样式。

¥Style to apply to the individual tab items in the tab bar.

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

¥By default, all tab items take up the same pre-calculated width based on the width of the container. If you want them to take their original width, you can specify width: 'auto' in tabStyle.

indicatorStyle

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

¥Style to apply to the active indicator.

indicatorContainerStyle

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

¥Style to apply to the container view for the indicator.

labelStyle

应用于选项卡项目标签的样式。

¥Style to apply to the tab item label.

contentContainerStyle

应用于选项卡内部容器的样式。

¥Style to apply to the inner container for tabs.

style(TabBar)

应用于选项卡栏容器的样式。

¥Style to apply to the tab bar container.

gap

定义选项卡之间的间距。

¥Define a spacing between tabs.

testID

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

¥Test id for the tabBar. Can be used for scrolling the tab bar in tests

优化技巧

¥Optimization Tips

避免不必要的重新渲染

¥Avoid unnecessary re-renders

每次索引更改时都会调用 renderScene 函数。如果你的 renderScene 函数很昂贵,那么最好将每个路由移动到一个单独的组件(如果它们不依赖于索引),并在你的路由组件中使用 shouldComponentUpdateReact.memo 以防止不必要的重新渲染。

¥The renderScene function is called every time the index changes. If your renderScene function is expensive, it's good idea move each route to a separate component if they don't depend on the index, and use shouldComponentUpdate or React.memo in your route components to prevent unnecessary re-renders.

例如,代替:

¥For example, instead of:

const renderScene = ({ route }) => {
switch (route.key) {
case 'home':
return (
<View style={styles.page}>
<Avatar />
<NewsFeed />
</View>
);
default:
return null;
}
};

请执行下列操作:

¥Do the following:

const renderScene = ({ route }) => {
switch (route.key) {
case 'home':
return <HomeComponent />;
default:
return null;
}
};

如果你使用类组件,则 <HomeComponent />PureComponent

¥Where <HomeComponent /> is a PureComponent if you're using class components:

export default class HomeComponent extends React.PureComponent {
render() {
return (
<View style={styles.page}>
<Avatar />
<NewsFeed />
</View>
);
}
}

或者,如果你使用函数组件,则封装在 React.memo 中:

¥Or, wrapped in React.memo if you're using function components:

function HomeComponent() {
return (
<View style={styles.page}>
<Avatar />
<NewsFeed />
</View>
);
}

export default React.memo(HomeComponent);

避免一帧延迟

¥Avoid one frame delay

我们需要测量容器的宽度,因此需要等待才能在屏幕上渲染某些元素。如果你预先知道初始宽度,则可以将其传入,我们不需要等待测量它。大多数时候,这只是窗口宽度。

¥We need to measure the width of the container and hence need to wait before rendering some elements on the screen. If you know the initial width upfront, you can pass it in and we won't need to wait for measuring it. Most of the time, it's just the window width.

例如,将以下 initialLayout 传递到 TabView

¥For example, pass the following initialLayout to TabView:

const initialLayout = {
height: 0,
width: Dimensions.get('window').width,
};

选项卡视图仍然会对尺寸的变化做出 React,并相应地进行调整以适应方向变化等情况。

¥The tab view will still react to changes in the dimension and adjust accordingly to accommodate things like orientation change.

优化大量路由

¥Optimize large number of routes

如果你有大量路由,尤其是图片,则可能会大大减慢动画速度。你可以改为渲染有限数量的路由。

¥If you've a large number of routes, especially images, it can slow the animation down a lot. You can instead render a limited number of routes.

例如,执行以下操作以在每侧仅渲染 2 条路由:

¥For example, do the following to render only 2 routes on each side:

const renderScene = ({ route }) => {
if (Math.abs(index - routes.indexOf(route)) > 2) {
return <View />;
}

return <MySceneComponent route={route} />;
};

避免在 ScrollView 内渲染 TabView

¥Avoid rendering TabView inside ScrollView

TabView 嵌套在垂直 ScrollView 内将禁用 TabView 内渲染的 FlatList 组件的优化。因此,如果可能的话,请避免这样做。

¥Nesting the TabView inside a vertical ScrollView will disable the optimizations in the FlatList components rendered inside the TabView. So avoid doing it if possible.

根据需要使用 lazyrenderLazyPlaceholder 属性渲染路由

¥Use lazy and renderLazyPlaceholder props to render routes as needed

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

¥The lazy option is disabled by default to provide a smoother tab switching experience, but you can enable it and provide a placeholder component for a better lazy loading experience. Enabling lazy can improve initial load performance by rendering routes only when they come into view. Refer the prop reference for more details.