react native实战项目 瀑布流、菜单吸顶、grid菜单、自定义背景图、tabbar底部菜单、轮播图

效果图

实现上面的UI需要如下

tabbar页面配置

复制代码
router/TabNavigator.tsx
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import Home from "../pages/home/HomeScreen";
// import CategoryScreen from "../screens/CategoryScreen";
// import CartScreen from "../screens/CartScreen";
import UserScreen from "../pages/user/UserScreen";

import ProfileIcon from "@/components/tabbar/ProfileIcon";
import iconDefault from '@assets/4-001.png';
import iconActive from '@assets/4-002.png';
import { s } from "@/utils/scale";
const Tab = createBottomTabNavigator();
// const iconActive=require('@/assets/4-002.png')
// const iconDefault=require('@/assets/4-001.png')
export default function TabNavigator() {
  return (
    <Tab.Navigator screenOptions={{ 
      headerShown:false, //隐藏默认导航栏
      tabBarActiveTintColor:"#0094FF",
      tabBarStyle: {
      height: s(60),           // 自定义高度
      paddingBottom: 0,     // 去掉默认 safe area padding
      paddingTop: 5,
    },
    tabBarLabelStyle: {
      fontSize: 12,         // 标签文字大小
    },
     }}>
      <Tab.Screen name="Home" component={Home} options={{ title:"首页" }}/>
      {/* <Tab.Screen name="Category" component={CategoryScreen} options={{ title:"分类" }}/>
      <Tab.Screen name="Cart" component={CartScreen} options={{ title:"购物车" }}/> */}
      {/* <Tab.Screen name="Profile" component={UserScreen} options={{ title:"我的" }}/> */}
      <Tab.Screen
  name="Profile"
  component={UserScreen}
  options={{
    title: "我的",
    tabBarIcon: ({ focused }) => <ProfileIcon focused={focused} defUrl={iconDefault} actUrl={iconActive} />,
  }}
/>
    </Tab.Navigator>
  );
}
自定义tabbar页面
StackNavigator。tsx配置
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import TabNavigator from "./TabNavigator";
import ProductDetail from "../pages/home/pages/deteail/product";
import { RootStackParamList } from "./type";

const Stack = createNativeStackNavigator<RootStackParamList>(); // ★ 类型加上

export default function StackNavigator() {
  return (
    <Stack.Navigator>
      <Stack.Screen name="Tabs" component={TabNavigator} options={{ headerShown:false }}/>
      <Stack.Screen name="Detail" component={ProductDetail} options={{ title:"商品详情" }}/>
    </Stack.Navigator>
  );
}


app.tsx
/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 */

import React from 'react';
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
import { StatusBar, StyleSheet } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';

import StackNavigator from './src/router/StackNavigator';

function App() {
  return (
    <SafeAreaProvider>
      {/* 状态栏样式,可根据主题调整 */}
      <StatusBar barStyle="dark-content" backgroundColor="#fff" />
      
      {/* SafeAreaView 全局包裹 */}
      <SafeAreaView style={styles.container}>
        <NavigationContainer>
          <StackNavigator />
        </NavigationContainer>
      </SafeAreaView>
    </SafeAreaProvider>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1, // 必须填充全屏
    backgroundColor: '#af2222ff', // App 默认背景色
  },
});

export default App;

页面目录 首页

HomeActivityZone.tsx

复制代码
// src/components/home/ActivityZone.tsx
import React from "react";
import { View, Text, Image, StyleSheet } from "react-native";
import LinearGradient from "react-native-linear-gradient";
import type { Activity } from "@/type/home.d";

type Props = { activities: Activity[] };
export default function ActivityZone({ activities = [] }: Props) {
  return (
    <View style={{ padding: 12 }}>
      {activities.map(act => (
        <View key={act.id} style={styles.card}>
          <View style={styles.header}>
            {act.cover ? <Image source={{ uri: act.cover }} style={styles.cover} /> : null}
            <View style={{ marginLeft: 8 }}>
              <Text style={styles.title}>{act.title}</Text>
              {act.subtitle ? <Text style={styles.sub}>{act.subtitle}</Text> : null}
            </View>
          </View>

          <View style={{ flexDirection: "row", marginTop: 8 }}>
            {act.products.slice(0, 2).map(p => (
              <LinearGradient key={p.id} colors={act.bgColors ?? ["#fff", "#fff"]} style={styles.product}>
                <Image source={{ uri: p.image }} style={styles.pImage} />
                <View style={{ flex: 1 }}>
                  <Text numberOfLines={2} style={styles.pTitle}>{p.title}</Text>
                  <Text style={styles.pSold}>销量 {p.sold}</Text>
                </View>
              </LinearGradient>
            ))}
          </View>
        </View>
      ))}
    </View>
  );
}
const styles = StyleSheet.create({
  card: { marginBottom: 12, borderRadius: 8, padding: 8, backgroundColor: "#fff", elevation: 2 },
  header: { flexDirection: "row", alignItems: "center" },
  cover: { width: 48, height: 48, borderRadius: 6 },
  title: { fontSize: 16, fontWeight: "600" },
  sub: { fontSize: 12, color: "#666" },
  product: { flex: 1, flexDirection: "row", padding: 8, borderRadius: 8, alignItems: "center", marginRight: 8 },
  pImage: { width: 64, height: 64, borderRadius: 6, marginRight: 8 },
  pTitle: { fontSize: 14, fontWeight: "600" },
  pSold: { fontSize: 12, color: "#666", marginTop: 6 },
});

homeBanner.tsx

复制代码
// src/components/home/BannerCarousel.tsx
import React, { useRef, useState } from "react";
import { View, FlatList, Image, Dimensions, StyleSheet } from "react-native";
import type { Banner } from "@/type/home.d";
const { width: SCREEN_W } = Dimensions.get("window");
type Props = { banners: Banner[]; indicatorColor?: string; height?: number };
export default function BannerCarousel({ banners = [], indicatorColor = "#fff", height = 180 }: Props) {
  const [active, setActive] = useState(0);
  const onViewRef = useRef(({ viewableItems }: any) => {
    if (viewableItems.length) setActive(viewableItems[0].index || 0);
  });
  return (
    <View style={{ width: SCREEN_W, height }}>
      <FlatList
        horizontal
        pagingEnabled
        data={banners}
        keyExtractor={(i) => i.id}
        showsHorizontalScrollIndicator={false}
        renderItem={({ item }) => <Image source={{ uri: item.image }} style={{ width: SCREEN_W, height }} />}
        onViewableItemsChanged={onViewRef.current}
        viewabilityConfig={{ viewAreaCoveragePercentThreshold: 50 }}
      />
      <View style={styles.dots}>
        {banners.map((_, i) => (
          <View key={i} style={[styles.dot, i === active ? { backgroundColor: indicatorColor, width: 18 } : { backgroundColor: "#fff", width: 8 }]} />
        ))}
      </View>
    </View>
  );
}
const styles = StyleSheet.create({
  dots: { position: "absolute", bottom: 8, left: 12, flexDirection: "row", alignItems: "center" },
  dot: { height: 8, borderRadius: 4, marginRight: 6 },
});

HomeGridMenu.tsx

复制代码
// src/components/home/GridMenuPager.tsx
import React, { useMemo } from "react";
import { View, FlatList, Image, Text, TouchableOpacity, StyleSheet, Dimensions } from "react-native";
import type { MenuItem } from "@/type/home.d";
const { width: SCREEN_W } = Dimensions.get("window");
type Props = { menus: MenuItem[]; onPress?: (item: MenuItem) => void };
export default function GridMenuPager({ menus = [], onPress }: Props) {
  const per = 8;
  const pages = useMemo(() => {
    const arr = [];
    for (let i = 0; i < menus.length; i += per) arr.push(menus.slice(i, i + per));
    return arr;
  }, [menus]);

  return (
    <View style={{ height: 200 }}>
      <FlatList
        data={pages}
        horizontal
        pagingEnabled
        showsHorizontalScrollIndicator={false}
        keyExtractor={(_, idx) => "page" + idx}
        renderItem={({ item }) => (
          <View style={[styles.page, { width: SCREEN_W }]}>
            {item.map((m) => (
              <TouchableOpacity key={m.id} style={styles.item} onPress={() => onPress?.(m)}>
                <Image source={{ uri: m.icon }} style={styles.icon} />
                <Text style={styles.title} numberOfLines={1}>{m.title}</Text>
              </TouchableOpacity>
            ))}
          </View>
        )}
      />
    </View>
  );
}
const styles = StyleSheet.create({
  page: { flexDirection: "row", flexWrap: "wrap", padding: 10, justifyContent: "space-between" },
  item: { width: (SCREEN_W - 40) / 4, height: 80, justifyContent: "center", alignItems: "center", marginBottom: 8 },
  icon: { width: 48, height: 48, borderRadius: 8, marginBottom: 6 },
  title: { fontSize: 12, textAlign: "center" },
});

HomeSearchBar.tsx

复制代码
// src/components/home/HomeSearchBar.tsx
import React from "react";
import { View, Text, TextInput, TouchableOpacity, StyleSheet, StatusBar } from "react-native";

type Props = {
  city?: string;
  onCityPress?: () => void;
  onSearch?: (q: string) => void;
};
export default function HomeSearchBar({ city = "定位中", onCityPress, onSearch }: Props) {
  return (
    <View style={[styles.container]}>
      <TouchableOpacity style={styles.city} onPress={onCityPress}>
        <Text style={styles.cityText}>{city}</Text>
      </TouchableOpacity>
      <View style={styles.inputWrap}>
        <TextInput
          placeholder="搜索商品、店铺"
          returnKeyType="search"
          style={styles.input}
          onSubmitEditing={(e) => onSearch?.(e.nativeEvent.text)}
        />
      </View>
    </View>
  );
}
const styles = StyleSheet.create({
  container: { marginTop: (StatusBar.currentHeight || 24) + 12, marginHorizontal: 12, flexDirection: "row", alignItems: "center", zIndex: 20 },
  city: { width: 80, height: 40, borderRadius: 20, backgroundColor: "#fff", justifyContent: "center", alignItems: "center", marginRight: 8 },
  cityText: { color: "#333" },
  inputWrap: { flex: 1, height: 40, borderRadius: 20, backgroundColor: "#fff", justifyContent: "center", paddingHorizontal: 12 },
  input: { height: 40, fontSize: 14 },
});

HomeTabMenu.tsx

复制代码
// src/components/home/ThreeLevelMenu.tsx
import React from "react";
import { View, ScrollView, TouchableOpacity, Text, StyleSheet } from "react-native";

type Props = {
  first: string[]; second: string[]; third: string[];
  activeFirst: number; activeSecond: number; activeThird: number;
  onFirst: (i:number)=>void; onSecond:(i:number)=>void; onThird:(i:number)=>void;
};
export default function ThreeLevelMenu({ first, second, third, activeFirst, activeSecond, activeThird, onFirst, onSecond, onThird }: Props) {
  return (
    <View style={{ backgroundColor: "#fff", padding: 8 }}>
        <Text>进来了吗</Text>
      <ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.row}>
        {first.map((t,i)=>(
          <TouchableOpacity key={t} style={[styles.chip, activeFirst===i && styles.chipActive]} onPress={()=>onFirst(i)}>
            <Text style={activeFirst===i?styles.chipTextActive:styles.chipText}>{t}</Text>
          </TouchableOpacity>
        ))}
      </ScrollView>

      <ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.row}>
        {second.map((t,i)=>(
          <TouchableOpacity key={t} style={[styles.chipSmall, activeSecond===i && styles.chipSmallActive]} onPress={()=>onSecond(i)}>
            <Text style={activeSecond===i?styles.chipTextActive:styles.chipTextSmall}>{t}</Text>
          </TouchableOpacity>
        ))}
      </ScrollView>

      <ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.row}>
        {third.map((t,i)=>(
          <TouchableOpacity key={t} style={[styles.chipSmall, activeThird===i && styles.chipSmallActive]} onPress={()=>onThird(i)}>
            <Text style={activeThird===i?styles.chipTextActive:styles.chipTextSmall}>{t}</Text>
          </TouchableOpacity>
        ))}
      </ScrollView>
    </View>
  );
}
const styles = StyleSheet.create({
  row:{maxHeight:48,marginBottom:6},
  chip:{paddingHorizontal:12,paddingVertical:8,borderRadius:20,backgroundColor:'#f2f2f2',marginRight:8},
  chipActive:{backgroundColor:'#FF6347'},
  chipText:{color:'#333'},
  chipTextActive:{color:'#fff'},
  chipSmall:{paddingHorizontal:10,paddingVertical:6,borderRadius:16,backgroundColor:'#f5f5f5',marginRight:8},
  chipSmallActive:{backgroundColor:'#FF6347'},
  chipTextSmall:{color:'#666'},
});

MasonryList.tsx

复制代码
// src/components/home/MasonryList.tsx
import React from "react";
import { FlatList, } from "react-native";
import type { Product } from "@/type/home.d";
import ProductCard from "./ProductCard";

export default function MasonryList({ products, onEndReached }: { products: Product[]; onEndReached?: ()=>void }) {
  return (
    <FlatList
      data={products}
      keyExtractor={(i)=>i.id}
      numColumns={2}
      columnWrapperStyle={{ justifyContent: "space-between", marginBottom: 12 }}
      renderItem={({ item }) => <ProductCard product={item} />}
      contentContainerStyle={{ paddingHorizontal: 10, paddingBottom: 60 }}
      onEndReached={onEndReached}
      onEndReachedThreshold={0.5}
      showsVerticalScrollIndicator={false}
    />
  );
}

ParallaxBackground.tsx

复制代码
import React from "react";
import { Animated, ImageBackground, StyleSheet, Dimensions } from "react-native";

const { width: SCREEN_W } = Dimensions.get("window");

type Props = {
  imageUri: string;
  translateY: Animated.AnimatedInterpolation<number>;
  height?: number;
};

export default function ParallaxBackground({ imageUri, translateY, height = 200 }: Props) {
  const src = typeof imageUri === "string" ? { uri: imageUri } : undefined;

  return (
    <Animated.View
      style={[
        styles.wrap,
        {
          height,
          transform: [{ translateY }],
          zIndex: -1, // 确保在所有内容下面
        },
      ]}
    >
      {src && (
        <ImageBackground
          source={src}
          style={[styles.bg, { height }]}
          resizeMode="cover"
        />
      )}
    </Animated.View>
  );
}

const styles = StyleSheet.create({
  wrap: {
    position: "absolute",
    top: 0,
    left: 0,
    width: SCREEN_W,
    overflow: "hidden",
  },
  bg: { width: "100%" },
});

ProductCard.tsx

复制代码
// src/components/home/ProductCard.tsx
import React from "react";
import { View, Text, Image, StyleSheet } from "react-native";
import type { Product  } from "@/type/home.d";

export default function ProductCard({ product }: { product: Product }) {
  const ratio = product.aspectRatio ?? 1;
  return (
    <View style={styles.card}>
      <Image source={{ uri: product.image }} style={[styles.image, { aspectRatio: ratio }]} resizeMode="cover" />
      <Text style={styles.title} numberOfLines={2}>{product.title}</Text>
      <View style={styles.row}>
        <Text style={styles.price}>¥{product.price}</Text>
        {product.oldPrice ? <Text style={styles.old}>¥{product.oldPrice}</Text> : null}
      </View>
      <Text style={styles.sold}>销量 {product.sold}</Text>
      <Text style={styles.shop} numberOfLines={1}>{product.shop} · {product.distanceKm}km</Text>
    </View>
  );
}
const styles = StyleSheet.create({
  card:{width:'48%', backgroundColor:'#fff', borderRadius:8, padding:8},
  image:{width:'100%', borderRadius:6, backgroundColor:'#eee', marginBottom:8},
  title:{fontSize:14,height:44,lineHeight:22},
  row:{flexDirection:'row',alignItems:'center'},
  price:{fontSize:16,fontWeight:'700',marginRight:8},
  old:{fontSize:12,textDecorationLine:'line-through',color:'#999'},
  sold:{fontSize:12,color:'#666',marginTop:4},
  shop:{fontSize:12,color:'#888',marginTop:6},
});

瀑布流

复制代码
import React, { useCallback } from "react";
import { View, ActivityIndicator, useWindowDimensions, Text } from "react-native";
import { FlashList } from "@shopify/flash-list";
import FastImage from "react-native-fast-image";

interface ItemProps {
  id: number | string;
  width: number;  // 原图宽
  height: number; // 原图高
  uri: string;
}

interface Props {
  data: ItemProps[];
  loadMore: () => void;
  loading: boolean;
  numColumns?: number;  // 默认2列
}

export default function WaterfallList({
  data,
  loadMore,
  loading,
  numColumns = 2,
}: Props) {

  const { width: screenWidth } = useWindowDimensions();
  const ITEM_MARGIN = 6;
  const itemWidth = (screenWidth - ITEM_MARGIN * (numColumns + 1)) / numColumns;

  const renderItem = useCallback(({ item }: { item: ItemProps }) => {
    const scale = item.height / item.width; // 计算高度比例
    return (
      <View style={{ width: itemWidth, margin: ITEM_MARGIN }}>
        
        <FastImage
          source={{ uri: item.uri }}
          style={{
            width: itemWidth,
            height: itemWidth * scale,  // 根据宽高比动态计算真实高度
            borderRadius: 8,
            backgroundColor: "#e5e5e5"
          }}
          resizeMode={FastImage.resizeMode.cover}
        />

        {/* 文字示例,让每项高度不一样 */}
        <Text numberOfLines={1} style={{ marginTop: 4, fontSize: 13 }}>
          哈哈哈哈 {item.id}
        </Text>
      </View>
    );
  }, [itemWidth]);
//  estimatedItemSize={200}  // ⚠ v2可不写,但写了更稳
  return (
    <FlashList<ItemProps>
      data={data}
      numColumns={numColumns}
      renderItem={renderItem}
      keyExtractor={(item) => item.id.toString()}
      onEndReached={() => !loading && loadMore()}
      onEndReachedThreshold={0.15}
      ListFooterComponent={
        loading ? <ActivityIndicator style={{ padding: 15 }} /> : null
      }
     
    />
  );
}

HomeScreen.tsx

复制代码
import {  Animated, StyleSheet, Text, View } from "react-native"

import ParallaxBackground from "./pages/components/ParallaxBackground";
import { useEffect, useRef, useState } from "react";
import { ms, s } from "@/utils/scale";
import HomeSearchBar from "./pages/components/HomeSearchBar";
import ThreeLevelMenu from "./pages/components/HomeTabMenu";
import ActivityZone from "./pages/components/HomeActivityZone";
import BannerCarousel from "./pages/components/HomeSearchBar";
import MasonryList from "./pages/components/MasonryList";
import GridMenuPager from "./pages/components/HomeGridMenu";
import { HomeModuleConfig, Product } from "@/type/home";
import { fetchHomeConfig } from "@/services/homeApi";

const MOCK_PRODUCTS: Product[] = new Array(30).fill(0).map((_,i)=>({
  id:'pd'+i, image:`https://picsum.photos/400/600?random=${i+50}`, title:`商品 ${i+1}`, price:(Math.random()*200).toFixed(2),
  oldPrice:(Math.random()*200+200).toFixed(2), sold:Math.floor(Math.random()*9999), shop:`店铺${i%5}`, distanceKm:Math.round(Math.random()*50)
}));
const Home=()=>{
    const imageUri="https://pics3.baidu.com/feed/b7fd5266d0160924969c0179408342f5e7cd34ae.jpeg@f_auto?token=ff69846d83ce6fcef343d438f6d42003"

    
const [config, setConfig] = useState<HomeModuleConfig[]>([]);
  const [bgImage, setBgImage] = useState<string | null>(null);

  useEffect(()=>{ fetchHomeConfig().then(cfg=>{ setConfig(cfg); const bg = cfg.find(c=>c.type==='background'); if(bg) setBgImage(bg.data?.image); }); },[]);

  // parallax
  const scrollY = useRef(new Animated.Value(0)).current;
  const translateY = scrollY.interpolate({
  inputRange: [0, 200],   // 滚动范围
  outputRange: [0, -100], // 位移范围(可实现视差)
  extrapolate: 'clamp'
});
  const bgTranslate = scrollY.interpolate({ inputRange:[0,200], outputRange:[0,-80], extrapolate:'clamp' });

  // three-level menu states
  const [first] = useState<string[]>(['分类A','分类B','分类C','分类D','分类E']);
  const [second,setSecond] = useState<string[]>(['子1','子2','子3']);
  const [third,setThird] = useState<string[]>(['三级1','三级2']);
  const [af,setAf] = useState(0); const [as,setAs] = useState(0); const [at,setAt] = useState(0);
  const [bottomProducts,setBottomProducts] = useState<Product[]>(MOCK_PRODUCTS);

  function onFirst(i:number){ setAf(i); setSecond(['子A','子B','子C']); setAs(0); }
  function onSecond(i:number){ setAs(i); setThird(['三级X','三级Y']); setAt(0); }
  function onThird(i:number){ setAt(i); setBottomProducts(MOCK_PRODUCTS.filter((_,idx)=>idx % 3 === i % 3)); }

  const onScroll = Animated.event([{ nativeEvent: { contentOffset: { y: scrollY } } }], { useNativeDriver: true });
    return(
        <View style={[styles.main,{ flex: 1 }]}>
          
            <ParallaxBackground imageUri={imageUri} translateY={translateY} />
            <Animated.ScrollView
        scrollEventThrottle={16}
        onScroll={onScroll}
        contentContainerStyle={{ paddingTop: 200 }} // keep space for parallax
      >
        {/* render modules from backend config */}
        {config.map((m, idx) => {
          if (!m.enabled && m.enabled !== undefined) return null;
          switch (m.type) {
            case "search": return <HomeSearchBar key={idx} city="台北市" />;
            case "banner": return <BannerCarousel key={idx} banners={m.data?.banners ?? []} indicatorColor={m.data?.indicatorColor} />;
            case "grid_menu": return <GridMenuPager key={idx} menus={m.data?.menus ?? []} />;
            case "activity": return <ActivityZone key={idx} activities={m.data?.activities ?? []} />;
            case "tab_menu": return (
              <View key={idx}>
                <Text>这是三级菜单</Text>
                <ThreeLevelMenu
                  first={first} second={second} third={third}
                  activeFirst={af} activeSecond={as} activeThird={at}
                  onFirst={onFirst} onSecond={onSecond} onThird={onThird}
                />
                <MasonryList products={bottomProducts} />
              </View>
            );
            default: return null;
          }
        })}
      </Animated.ScrollView>
        </View>
    )
}
const styles=StyleSheet.create({
    main:{
        width:"100%",height:"100%"
    },
    connc:{
        height:s(800)
    },
    text:{
        fontSize:ms(24)
    }
})
export default Home;

运行效果


插件说明

复制代码
yarn add @shopify/flash-list   //瀑布流插件
yarn add react-native-fast-image  # 建议替换 Image,更快更省内存

当前项目依赖说明

包名 用途说明 是否建议保留
react / react-native RN 必备核心 ✔ 必须
@shopify/flash-list 大数据高性能列表,比 FlatList / VirtList 更强 ✔ 推荐
react-native-fast-image 更快 & 支持缓存的图片组件 ✔ 推荐
@react-navigation/native React Native 路由核心库 ✔ 必备(如需导航)
@react-navigation/native-stack 原生动画 + 更快的栈导航 ✔ 推荐替代 createStackNavigator
@react-navigation/bottom-tabs 底部 TabBar 导航 ✔ 推荐
react-native-safe-area-context 用于处理 iOS 安全区(刘海屏适配) ✔ 推荐
react-native-screens 导航性能优化,配合 navigation 必装 ✔ 推荐
react-native-linear-gradient 渐变背景组件 UI美化必备 ✔ 好用可留
react-native-responsive-screen vw/vh 响应式布局 hp()/wp() ⚠ 可用但推荐 react-native-responsive-dimensions
react-native-size-matters dp→自动缩放,随屏幕大小适配 ✔ 新 UI 适配友好

项目已装的核心库 --- 必会用法示例

  1. ① FlashList --- 高性能列表

    import { FlashList } from "@shopify/flash-list";

    <FlashList
    data={list}
    renderItem={({ item }) => <Item data={item}/>}
    estimatedItemSize={120} // ← 必写(渲染速度差异巨大)
    />

  2. FastImage --- 图片带缓存、加载更快

    import FastImage from "react-native-fast-image"

    <FastImage
    source={{ uri:'https://xx.jpg', priority:'high' }}
    style={{ width:120, height:180 }}
    resizeMode={FastImage.resizeMode.cover}
    />

  3. Navigation --- 路由使用示例

    import { NavigationContainer } from '@react-navigation/native';
    import { createNativeStackNavigator } from '@react-navigation/native-stack';

    const Stack = createNativeStackNavigator();

    export default () => (
    <NavigationContainer>
    <Stack.Navigator>
    <Stack.Screen name="Home" component={Home}/>
    <Stack.Screen name="Detail" component={Detail}/>
    </Stack.Navigator>
    </NavigationContainer>
    );

  4. 渐变背景 LinearGradient

    import LinearGradient from 'react-native-linear-gradient'

    <LinearGradient
    colors={['#4facfe', '#00f2fe']}
    style={{ padding:20, borderRadius:10 }}

    <Text style={{color:'#fff'}}>按钮</Text>
    </LinearGradient>

  5. 响应式适配 size-matters

    import { scale, verticalScale } from "react-native-size-matters";

    <View style={{ width: scale(120), height: verticalScale(80) }} />

RN项目常用插件

插件 用途
zustand / jotai / recoil 更轻更快的全局状态管理
react-native-mmkv 替代 AsyncStorage,读写100x更快
react-native-reanimated 高性能动画 + 手势
react-native-gesture-handler 官方手势库 必备
lottie-react-native 高级动画 UI精致必备
react-native-device-info 获取设备型号、唯一ID
react-native-localize 多语言 i18n 适配
react-native-bootsplash 启动闪屏过渡优化
react-native-permissions 权限统一处理(相机/存储/GPS)
相关推荐
苏打水com1 小时前
第四篇:Day10-12 JS事件进阶+CSS动画——实现“复杂交互+视觉动效”(对标职场“用户体验优化”需求)
javascript·css·交互
BD_Marathon1 小时前
【JavaWeb】JavaScript使用var声明变量的特点
javascript
黛色正浓1 小时前
【React】极客园案例实践-发布文章模块
前端·react.js·前端框架
开发者小天1 小时前
react的组件库antd design表格多选,删除的基础示例
前端·javascript·react.js
by__csdn1 小时前
Vue3响应式系统详解:ref与reactive全面解析
前端·javascript·vue.js·typescript·ecmascript·css3·html5
渴望成为python大神的前端小菜鸟2 小时前
react 面试题
前端·react.js·前端框架·react·面试题
chilavert3182 小时前
技术演进中的开发沉思-231 Ajax:页面内容修改
开发语言·前端·javascript
m0_376137942 小时前
DevUI主题系统进阶:CSS-in-JS与暗黑模式无缝切换架构
javascript·css·架构·devui
晚霞的不甘2 小时前
实战深研:构建高可靠、低延迟的 Flutter + OpenHarmony 智慧教育互动平台(支持离线教学、多端协同与国产化适配)
前端·javascript·flutter