Taro跨端开发探索11——商城小程序首页实现

512 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第17天,点击查看活动详情

前言

昨天我们实现了一个简单的首页多模块异步加载的功能,今天我们继续探索首页下拉加载数据的实现

需求分析

我们现在要实现下拉刷新,可以放在home首页,也可以放在推荐商品页面。根据我们的观察,在下拉的时候,京喜的固定活动也会向上移动,而且如果没有推荐商品或者查询不出推荐商品时会显示没有数据的标识。
所以,我们需要实现的功能为

  • 首页可以下拉加载推荐商品的数据
  • 加载数据的时候在底部显示【加载中】,加载完成或者查询不出数据的时候显示【暂无更多数据】

功能实现

ScrollView

我们使用taro的ScrollView来实现下拉加载。虽然组件时taro的,但是组件中的大部分属性是微信小程序原生(developers.weixin.qq.com/miniprogram…

  • 给scroll-view设置固定的高度
  • scrollY 设置为true才能向Y轴移动
  • onScrollToLower 达到底部的回调

代码示例与逻辑说明

home/index.scss 设置ScrollView的固定高度

.home {
  width: 100%;
  height: 1100px;
}

home/index.tsx

import { ScrollView, Text } from "@tarojs/components";
import { useState } from "react";
import "./index.scss";
import RecommendedGoods from "./recommended-goods";
import ResidentActivities from "./resident-activities";
export default function Home() {
  const [goodQueryIndex, setGoodsQueryIndex] = useState(1);
  const [bottomLoading, setBottomLoading] = useState({
    loading: false,
    hasMore: true,
  });
  return (
    <ScrollView
      className="home"
      scrollY={true}
      onScrollToLower={() => {
        setGoodsQueryIndex(goodQueryIndex + 1);
      }}
    >
      <ResidentActivities />
      <RecommendedGoods
        queryIndex={goodQueryIndex}
        bottomLoadingCallBack={(value) => setBottomLoading(value)}
      />
      {bottomLoading.loading ? (
        <Text>加载中</Text>
      ) : bottomLoading.hasMore ? (
        ""
      ) : (
        <Text>没有更多了</Text>
      )}
    </ScrollView>
  );
}
  • goodQueryIndex 推荐商品查询的当前分页页数,这个值默认设置为了1。子组件监听了props.queryIndex,所以页面初始化的时候就会异步记载推荐商品的数据。
  • bottomLoading 底部加载的状态管理,其中load属性控制是否显示【加载中】,hasMore控制是否显示【没有更多了】
  • onScrollToLower 到达底部的时候将goodQueryIndex+1,子组件通过监听props.queryIndex继续加载数据
  • bottomLoadingCallBack 子组件的回调处理,子组件在加载数据的时候设置bottomLoading.load为true,加载完成根据后端返回的数据判断是否能够继续加载。可以加载的话bottomLoading.hasMore为true
  • 注意点 我这里通过判断goodQueryIndex>5的方式限制了数据的加载。大家可以通过后端返回的分页数据中的共多少页来限制加载 home/recommended-goods/index.tsx
import { View, Text } from "@tarojs/components";
import { useEffect, useState } from "react";
import { AtImagePicker } from "taro-ui";
import "./index.scss";
export default function RecommendedGoods(props: any) {
  const [loading, setLoading] = useState(true);
  const [data, setData] = useState([] as any);
  useEffect(() => {
    if (props.queryIndex > 5) {
      props.bottomLoadingCallBack({ loading: false, hasMore: false });
      return;
    }
    props.bottomLoadingCallBack({ loading: true, hasMore: true });
    const timer = setTimeout(() => {
      clearTimeout(timer);
      setLoading(false);
      const loadData = [
        {
          id: 111 + "" + props.queryIndex,
          photo: "",
          title: `第${props.queryIndex}页,第一个商品`,
          price: props.queryIndex * 100,
        },
        {
          id: 222 + "" + props.queryIndex,
          photo: "",
          title: `第${props.queryIndex}页,第二个商品`,
          price: props.queryIndex * 200,
        },
        {
          id: 333 + "" + props.queryIndex,
          photo: "",
          title: `第${props.queryIndex}页,第三个商品`,
          price: props.queryIndex * 300,
        },
        {
          id: 444 + "" + props.queryIndex,
          photo: "",
          title: `第${props.queryIndex}页,第四个商品`,
          price: props.queryIndex * 400,
        },
      ];
      setData((data) => [...data, ...loadData]);
      props.bottomLoadingCallBack({ loading: false, hasMore: true });
    }, 2000);
  }, [props.queryIndex]);

  return (
    <View className="recommended-goods">
      {loading ? (
        ""
      ) : (
        <View>
          <View>
            {data.map((item) => (
              <View>
                <AtImagePicker
                  files={[
                    {
                      url: "https://storage.360buyimg.com/mtd/home/111543234387022.jpg",
                    },
                  ]}
                  showAddBtn={false}
                  onImageClick={() => {
                    console.log(item["id"]);
                  }}
                  count={1}
                  onChange={() => {}}
                  sizeType={["original", "compressed"]}
                />
                <Text>
                  {item["title"]}:{(item["price"] / 100).toFixed(2)}
                </Text>
              </View>
            ))}
          </View>
        </View>
      )}
    </View>
  );
}

子组件中没有什么需要特殊解释的地方,大部分都和上边一起解释了。以为我们在父组件定义了显示【加载中】的地方,所以此处我们设置子组件在loading时显示空字符串

结语

今天我们实现了首页的数据下拉加载,大家如果有兴趣可以试一下上拉刷新。也挺简单的,我这里就不水文了。
我们明天开始开发通用的搜索页面,欢迎大家关注本系列文章。
本系列文章gitee地址为:gitee.com/liangminghu…
最后给大家推荐一个我基于【码上掘金】开发的小游戏,欢迎大家体验:juejin.cn/post/708746…