RN实战开发

07-首页--网络请求及轮播广告

2021-08-24  本文已影响0人  Right01

快乐的开始开发首页之前,先处理一下底部标签tab的图片

了解一下 react-native-vector-icons 矢量图标库
添加矢量图库插件

# 添加依赖库
yarn add react-native-svg

# 安装 字体组件工具
yarn add -D react-native-iconfont-cli

# 涉及 原生pod依赖,执行一下pod
./pod.sh

# 创建一个iconfont.js
npx iconfont-init

修改配置iconfont.js
修改配置 iconfont.js 的 图库的url。以及是否使用typeScript,和 保存路径。

{
    "symbol_url": "http://at.alicdn.com/t/font_2710555_n7b6g9ja71.js",
    "use_typescript": true,
    "save_dir": "./src/assets/iconfont",
    "trim_icon_prefix": "",
    "default_icon_size": 18,
    "local_svgs": ""
}

执行 npx iconfont-rn 生成 图片组件

npx iconfont-rn

使用图片组件

给底部tab都添加上tabBarIcon,设置Icon

<Tab.Screen
      name="Home"
      component={Home}
      options={{
            tabBarLabel: '首页',
            tabBarIcon: ({ color, size }) => (
                  <IconIconHome color={color} size={size} />
            ),
      }}
/>

还有我听,发现,账户,同样的设置,就不贴重复代码了


image.png

首页开发

首页顶部存在 tab,这时需要用到顶部标签导航器

顶部标签导航器

添加依赖

yarn add @react-navigation/material-top-tabs react-native-tab-view react-native-pager-view

# 涉及原生pod引用,需要pod install
./pod.sh

创建src/navigator/HomeTabs.tsx 文件

import React from "react";
import { createMaterialTopTabNavigator } from "@react-navigation/material-top-tabs";
import Home from "@/pages/Home";

//声明变量,接收函数返回值
const Tab = createMaterialTopTabNavigator();

class HomeTabs extends React.Component {
    render() {
        return (
            // {/* 'tabBarOptions' is deprecated. Migrate the options to 'screenOptions' instead. */}
            <Tab.Navigator
                screenOptions={{
                    lazy: true,
                    tabBarScrollEnabled: true,
                    tabBarItemStyle: {
                        width: 80,
                    },
                    //tab底部横条样式
                    tabBarIndicatorStyle: {
                        height: 4,
                        width: 20,
                        marginLeft: 30,
                        borderRadius: 2,
                        backgroundColor: '#f86442',
                    },
                    tabBarActiveTintColor: '#f86442',
                    tabBarInactiveTintColor: '#333333',
                }}
            >
                <Tab.Screen name="Home" component={Home} options={{ tabBarLabel: '推荐' }} />
                <Tab.Screen name="Home1" component={Home} />
                <Tab.Screen name="Home2" component={Home} />
            </Tab.Navigator>
        );
    }
}
export default HomeTabs;

src/navigator/BottomTabs 中首页视图替换成HomeTabs

<Tab.Screen
    name="HomeTabs"
    component={HomeTabs}
    options={{
             tabBarLabel: '首页',
             tabBarIcon: ({ color, size }) => (
                 <IconIconHome color={color} size={size} />
            ),
    }}
/>

首页轮播图

yarn add react-native-snap-carousel

# 引入这个库的 申明文件
yarn add @types/react-native-snap-carousel -D
import { hp, viewPortWidth, wp } from "@/utils/index";
import React from "react";
import { Image, Platform, StyleSheet, View } from "react-native";
import SnapCarousel, { AdditionalParallaxProps, Pagination, ParallaxImage } from "react-native-snap-carousel";

// 假数据
const data = [
    "https://image2.baidu.com/uimg/cms/img/162729136945485175.png",
    "https://image2.baidu.com/uimg/cms/img/162743538431638633.png",
    "https://image2.baidu.com/uimg/cms/img/162737580088177971.png",
    "https://image2.baidu.com/uimg/cms/img/162703143520178503.png",
];

const sliderWidth = viewPortWidth;
const sideWidth = wp(90);
const sideHeight = hp(15);
const itemWidth = sideWidth + wp(2) * 2;


class Banner extends React.Component {

    state = {
        activeSlide: 0,
    }

    onSnapToItem = (index: number) => {
        this.setState({
            activeSlide: index,
        });
    }

    renderItem = ({ item }: { item: string }, parallaxProps?: AdditionalParallaxProps) => {
        return (
            <ParallaxImage
                source={{ uri: item }}
                style={styles.image}
                containerStyle={styles.imageContainer}
                parallaxFactor={0.4}
                showSpinner
                spinnerColor='rgba(0, 0, 0, 0.25)'
                {...parallaxProps}
            />
        );
    }

    //定位点
    get pagination() {
        const { activeSlide } = this.state;
        return (
            <View style={styles.paginationWraper}>
                <Pagination
                    containerStyle={styles.paginationContainer}
                    dotsLength={data.length}
                    dotContainerStyle={styles.dotContainer}
                    dotStyle={styles.dot}
                    activeDotIndex={activeSlide}
                    inactiveDotScale={0.8}
                    inactiveDotOpacity={0.4}
                />
            </View>
        );
    }

    render() {
        return (
            <View>
                <SnapCarousel
                    data={data}
                    renderItem={this.renderItem}
                    sliderWidth={sliderWidth}
                    itemWidth={itemWidth}
                    onSnapToItem={this.onSnapToItem}
                    hasParallaxImages
                    loop
                    autoplay
                />
                {this.pagination}
            </View>
        );
    }
}
// 定义样式
const styles = StyleSheet.create({
    imageContainer: {
        width: itemWidth,
        height: sideHeight,
        marginBottom: Platform.select({ ios: 0, android: 1 }),
        backgroundColor: 'white',
        borderRadius: 12,
    },
    image: {
        ...StyleSheet.absoluteFillObject,
        resizeMode: 'contain',
    },
    paginationWraper: {
        justifyContent: 'center',
        alignItems: 'center',
    },
    paginationContainer: {
        position: 'absolute',
        top: -20,
        backgroundColor: 'rgba(0, 0, 0, 0.35)',
        paddingHorizontal: 3,
        paddingVertical: 4,
        borderRadius: 8
    },
    dotContainer: {
        marginHorizontal: 6,
    },
    dot: {
        width: 6,
        height: 6,
        borderRadius: 3,
        backgroundColor: 'rgba(255, 255, 255, 0.92)',
    }
})

export default Banner;

网络请求---- 后台数据

对于一个APP,假数据是肯定不行的,需要从后台请求网络数据,展示到页面上
如果是用于自己学习的,没有服务端数据, 可以 研究一下 Yapi 接口管理平台

yarn add axios
import axios from "axios";
import Config from "react-native-config";
// 配置ip:端口
axios.defaults.baseURL = Config.API_BASE_URL;
// 配置请求拦截器
axios.interceptors.request.use(function (config) {
    console.log("http.ts:_______请求配置:" + config);
    return config;
}, function (error) {
    return Promise.reject(error);
})
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    console.log("http.ts:_______响应数据:" + response);
    return response.data;
}, function (error) {
    return Promise.reject(error);
})
//引入插件
import axios from "axios";
// body 子节点数据模型
export interface HomeBodyItem{
    item: Object,
    itemType: string,
    sourceModuleType: string,
}
export interface HeaerItemBannerModel
{
    cover: string,
    link: string,
    realLink: string, //路由连接
    name: string,
}
// Banner 广告数据节点模型
export interface HeaerItemBanner
{
    data: HeaerItemBannerModel[],
    responseId: number,
}
// header 子节点的 item 数据模型
export interface HeaerItemModel {
    moduleType: string,
    title: string,
    list: [],//
}
// header 子节点数据模型
export interface HomeHeaderItem {
    item: HeaerItemModel,
    itemType: string,
}
//数据根节点
export interface HomeState {
    header: HomeHeaderItem[],
    body: HomeBodyItem[],
}

调用接口封装

import { Effect, Model } from "dva-core-ts";
import { Reducer } from "redux";
import axios from "axios";

const HOME_URL = "/mock/22/home";

interface HomeModel extends Model {
    namespace: 'home';  //不可改变,唯一的
    state: HomeState;  //home的状态,所有数据都会保存到state中
    reducers: {
        setState: Reducer<HomeState>;
    };  //处理同步工作的action
    effects: {
        fetchHome: Effect;
    }; //处理异步工作的,如异步请求
}

const initState = {
    header: [],
    body: [],
}

const homdeModel: HomeModel = {
    namespace: 'home',
    state: initState,
    reducers: {
        setState(state = initState, { payload, type }) {
            return {
                ...state,
                ...payload,
            };
        },
    },
    effects: {
        //第一个参数 action,第二个参数,主要是用来完成异步操作
        //等3秒执行 add
        *fetchHome(_, { call, put }) {
            const { header, body} = yield call(axios.get, HOME_URL);
            // console.log("___________首页数据header:", header);
            // console.log("___________首页数据body:", body);
            yield put({
               type: 'setState',
               payload: {
                   header: header,
                   body: body,
               } 
            });
        },
    },
}
export default homdeModel;
//拿到 models 中 home的 num 值
const mapStateToProps = ({ home, loading }: RootState) => ({
    header: home.header,
    body: home.body,
    loading: loading.effects['home/fetchHome'],
});

// ...
/**
 * 首页类
 */
class Home extends React.Component<IProps> {
    componentDidMount() {
        const {dispatch} = this.props;
        dispatch({
            type: "home/fetchHome",
        });
    }
    onPress = () => {
        const { navigation } = this.props;
        navigation.navigate("Detail", { id: 100 });
    }
    
    render() {
        const { header, body, loading } = this.props;
        var banner: HeaerItemBannerModel[] = [];
        var icons: HeaerItemBannerModel[] = [];
        header.forEach(element => {
            //banner
            if (element.item.moduleType == 'focus' && element.item.list.length > 0) {
                //banner广告
                element.item.list.forEach((ele: HeaerItemBanner) => {
                    banner = ele.data;
                });
            }
            //icon广告
            else if (element.item.moduleType == 'square' && element.item.list.length > 0) {
                //广告Icon
                icons = element.item.list;
            }
        });

        return (
            <View>
                <Text>Home</Text>
                <Button title="跳转到详情页" onPress={this.onPress}></Button>
                <Banner banner={banner}/>

            </View>
        );
    }
}
interface IProps {
    banner: HeaerItemBannerModel[],
}

class Banner extends React.Component<IProps> {
    ...
    renderItem = ({ item }: { item: HeaerItemBannerModel }, parallaxProps?: AdditionalParallaxProps) => {
        return (
            <ParallaxImage
                source={{ uri: item.cover }}
                ...
            />
        );
    }

    //定位点
    get pagination() {
        const { banner } = this.props;
        ...
        return (
            <View style={styles.paginationWraper}>
                <Pagination
                    ...
                    dotsLength={banner.length}
                    ...
                />
            </View>
        );
    }

    render() {
        const { banner } = this.props;
        if (banner != null && banner.length > 0) {
            return <View>
                <SnapCarousel
                    data={banner}
                    ... />
                {this.pagination}
            </View>;
        }
        else {
            return null;
        }
    }
}

效果图


image.png

ps: 可能存在图片加载不出来,请修改iOS工程的配置文件info.plist

<key>NSAppTransportSecurity</key>
<dict>
      <key>NSAllowsArbitraryLoads</key>
      <true/>
</dict>

重新运行./yarnios.sh

ps: 待完善,一步一个👣👣👣,up~

上一篇下一篇

猜你喜欢

热点阅读