通用H5落地活动页设计与实现

103 阅读2分钟

背景

公司都有运营拉流量的需求,一般落地活动页也就是单页面应用,不会涉及到很复杂逻辑数据交互,但是别看是一个小小的活动页,作者刚毕业,也是被狠狠拷打了一番,下面就作者实践思考之后,沉淀出一些在开发遇到的坑,和通用的设计方案,减少被PM和QA狠狠diss。

需求设计

对于活动页面,常常需要几个变量来控制状态,如:loading(加载数据), pageError(出现未知错误), getData(获取数据),以及empty(空数据)状态。
流程设计:

image.png 流程说明: 用户进入到我们的页面,初始状态应该是一个loading状态,并且在React or Vue生命周期的时候去请求接口数据,在数据返回回来时根据数据状态,来判断页面该显示什么样子。 以下是对通用的hook进行说明

import React, { useState } from 'react';

export interface Data {
    items: number[];
    total: number;
    page: number;
    pageSize: number;
}
interface Props {
    fetch: (params: any) => Promise<Data>;
}

export const useHooks = ({ fetch }: Props) => {
    const [loading, setLoading] = useState<boolean>(true);
    const [error, setError] = useState<boolean>(false);
    const [dataObject, setDataObject] = useState<Data>({} as Data);

    const getData = async (params: any) => {
        setLoading(true);
        // 必须try catch 万一 QA给你来个断网 不炸锅了吗?
        try {
            const res = await fetch(params);
            const { items, total, page, pageSize } = res;
            setDataObject({
                items: dataObject.items.concat(items),
                total,
                page,
                pageSize
            });
        } catch {
            setError(true);
        } finally {
            setLoading(false);
        }
    };
   
    const isNeedFetch = () => {
        if (loading) {
            return false;
        }
        let needFetch = false;
        const scrollItme = document.getElementById('scroll') || {
            scrollTop: 0,
            clientHeight: 0,
            scrollHeight: 0
        };
        if (scrollItme.scrollTop + scrollItme.clientHeight > scrollItme.scrollHeight - 150) {
            needFetch = true;
        }
        return needFetch;
    };
    // 上拉加载
    const nextFetch = async (params: any) => {
        if (!isNeedFetch()) {
            return;
        }
        await getData(params);
    };

    return {
        loading,
        error,
        getData,
        nextFetch,
        dataObject
    };
};

import React, { useEffect } from 'react';
import { Data, useHooks } from './useHooks';
import { Empty } from 'antd';
const api = (params: any) =>
    new Promise((resolve, reject) => {
        resolve({
            items: [],
            total: 100,
            page: 1,
            pageSize: 10
        });
    }) as Promise<Data>;
export const Landing = () => {
    const { loading, error, getData, nextFetch, dataObject } = useHooks({ fetch: api });
    // 下拉刷新
    const handleScroll = (e: any) => {
        const scrollElement = e.currentTarget;
        if (scrollElement.scrollTop <= 10 && !loading) {
            return;
        }
        nextFetch({});
    };
    useEffect(() => {
        getData({
            page: 1,
            pageSize: 10
        });
    }, []);
    return (
    // 这里可以二次封装,让页面只关心list 对error,empty loading封装到组件内部
        <div>
            {error && <Error />}

            {dataObject && dataObject.items.length === 0 ? (
                <Empty />
            ) : (
                <div id="scroll" onScroll={handleScroll}>
                    {dataObject?.items.map((item, index) => (
                        <div key={index}>{item}</div>
                    ))}
                </div>
            )}
            {loading && <Loading />}
        </div>
    );
};

总结

业务上 也主要实现了Error loading 以及实现下拉加载,当然了,在这个过程中,如果在Error组件出现的时候,也应该实现重新加载数据,这种情况可以再考虑吧useHooks封装到一个context里面,这样组件共享状态,使得出了错误也能重试。当然 如果有多个类似相同元素,比如直播活动等等,可以考虑封装一个公用的组件负责loading Error 下拉加载,这样开发就只需要关注获取list,无需关注Error,loading等,从而提高开发效率。