美文网首页RN实战开发
07-首页--网络请求及轮播广告

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

作者: Right01 | 来源:发表于2021-08-24 17:26 被阅读0次

    快乐的开始开发首页之前,先处理一下底部标签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
    
    • 封装一个Banner轮播广告控件,自动轮播,定位点
      创建文件src/pages/Home/Banner.tsx 控件
    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
    
    • 封装请求 src/confing/http.ts
    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);
    })
    
    • src/index.tsx导入 http.ts

    • src/models/home.ts调用接口

    //引入插件
    import axios from "axios";
    
    • 修改/src/models/home.ts
      根据接口数据结构,定义数据模型
    // 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;
    
    • 首页/src/page/Home/Home.tsx 调用首页接口,并给Banner控件赋值
    //拿到 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>
            );
        }
    }
    
    • 修改Banner.tsx 数据源,展示banner广告数据
    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~

    相关文章

      网友评论

        本文标题:07-首页--网络请求及轮播广告

        本文链接:https://www.haomeiwen.com/subject/fwpdiltx.html