taro微信程序实用工具

30 阅读3分钟

1.封装小程序动画hook(支持异步调用)

import Taro from '@tarojs/taro';
import {useCallback, useEffect, useRef, useState} from 'react';

export default function useAsyncAnimation(id = 'default') {
    const [animationData, setAnimationData] = useState({});
    const animationInstance = useRef({});
    const animationPromiseRef = useRef({});
    const timer = useRef(null);

    useEffect(() => {
        return () => {
            clearTimeout(timer.current);
        };
    }, []);

    const getAnimation = useCallback(() => {
        if (!animationInstance.current[id]) {
            animationInstance.current[id] = Taro.createAnimation({
                duration: 500,
                timingFunction: 'ease',
            });
        }
        return animationInstance.current[id];
    }, [id]);

    const animateStep = useCallback(
        async (actions, options = {}) => {
            // 如果已有动画在进行,等待它完成
            if (animationPromiseRef.current[id]) {
                await animationPromiseRef.current[id];
            }

            const anim = getAnimation();
            animationPromiseRef.current[id] = new Promise((resolve) => {
                // 执行动画动作
                actions(anim);

                // 设置动画步骤
                anim.step({
                    duration: options.duration || 500,
                    timingFunction: options.timingFunction || 'ease',
                    delay: options.delay || 0,
                });

                timer.current = setTimeout(() => {
                    animationPromiseRef.current[id] = null;
                    resolve();
                    clearTimeout(timer.current);
                }, options.duration || 500);

                // 立即更新动画数据
                setAnimationData(anim.export());
            });

            return animationPromiseRef.current[id];
        },
        [getAnimation, id],
    );

    const reset = useCallback(() => {
        const anim = getAnimation();
        anim.step({duration: 0});
        setAnimationData(anim.export());
    }, [getAnimation]);

    return {
        animationData,
        animateStep,
        reset,
    };
}

调用案例

import {useAsyncAnimation} from '@hooks';

export default function GuaShark({onSubmit}) {
    const {animationData: decAnime, animateStep: decAnimeStep} =
            useAsyncAnimation('decBox');
            
    useEffect(() => {
        startAnime();
    }, []);
    
    const startAnime = () => {
        await decAnimeStep((anim) => anim.opacity(0), {duration: 800});
        await decAnimeStep((anim) => anim.opacity(1), {duration: 800});
    }

    return (
        <View className='dec-box' animation={decAnime}></View>
    )
}

2. 订单轮询

调用机制:支付成功的回调里轮询订单,确保订单是已支付状态。

export function paymentPoll(orderId, queryFuc, succCallBack) {
    let attempts = 0;
    let timer = null;
    const maxAttempts = 10;

    // 验证订单是否支付成功
    const isPaymentSuccess = (order) => {
        return order.status === 'paid';
    };
    // 显示加载提示
    Taro.showLoading({
        title: '支付确认中...',
        mask: true,
    });
    // 执行轮询
    const poll = async () => {
        attempts++;
        // 更新加载提示
        Taro.showLoading({
            title: `支付确认中...(${attempts}/${maxAttempts})`,
            mask: true,
        });
        try {
            const orderRes = await queryFuc(orderId);
            if (isPaymentSuccess(orderRes?.data)) {
                // 支付成功
                Taro.hideLoading();
                clearTimeout(timer);
                Taro.showToast({
                    title: '支付成功',
                    icon: 'success',
                    duration: 2000,
                });
                // 执行成功回调
                setTimeout(() => {
                    succCallBack(orderRes?.data);
                }, 1500);
                return;
            }
            // 检查是否支付失败
            if (orderRes?.data?.status.status === 'created') {
                Taro.hideLoading();
                clearTimeout(timer);
                Taro.showToast({
                    title: '支付失败',
                    icon: 'error',
                    duration: 3000,
                });
                return;
            }

            // 检查是否超过最大尝试次数
            if (attempts >= maxAttempts) {
                Taro.hideLoading();
                clearTimeout(timer);
                Taro.showToast({
                    title: '支付确认超时',
                    icon: 'none',
                    duration: 3000,
                });
                return;
            }
            // 继续轮询
            timer = setTimeout(poll, 2000);
        } catch (error) {
            Taro.hideLoading();
            clearTimeout(timer);
            console.error('查询订单状态失败:', error);
            Taro.showToast({
                title: '网络错误,请稍后检查',
                icon: 'none',
                duration: 3000,
            });
        }
    };
    // 开始轮询
    poll();
    // 返回停止函数(可选)
    return {
        stop: () => {
            if (timer) clearTimeout(timer);
            Taro.hideLoading();
        },
    };
}

3.小程序分包

一、什么是分包?

小程序分包是指将小程序代码分成多个包,在构建时打包成不同的分包,用户使用时按需加载。

二、分包的优势

  1. 优化首次加载时间:主包只包含核心功能,分包按需加载
  2. 代码组织更清晰:功能模块化,便于团队协作
  3. 提高开发效率:可以并行开发不同分包
  4. 避免包体积过大:突破主包2M限制(微信小程序为例)

三、分包配置

1. 目录结构

text

project/
├── app.js
├── app.json
├── app.wxss
├── pages/           # 主包页面
│   ├── index/
│   └── logs/
├── packageA/        # 分包A
│   ├── pages/
│   │   ├── cat/
│   │   └── dog/
│   └── utils/
├── packageB/        # 分包B
│   ├── pages/
│   │   ├── apple/
│   │   └── banana/
│   └── components/
└── common/          # 公共代码(建议放主包)

2. app.json 配置示例

json

{
  "pages": [
    "pages/index",
    "pages/logs"
  ],
  "subpackages": [
    {
      "root": "packageA",
      "pages": [
        "pages/cat",
        "pages/dog"
      ],
      "name": "packA",  // 分包别名(非必需)
      "independent": false  // 是否独立分包
    },
    {
      "root": "packageB",
      "pages": [
        "pages/apple",
        "pages/banana"
      ]
    }
  ],
  "preloadRule": {
    "pages/index": {
      "network": "all",
      "packages": ["packageA"]
    }
  }
}

四、分包限制和注意事项

1. 包大小限制(微信小程序):

  • 整个小程序所有分包大小不超过 20M
  • 单个分包/主包大小不超过 2M

2. 打包原则:

  • 主包:app.jsapp.jsonapp.wxsstabBar页面、公共资源

  • 分包:私有页面、组件、资源

  • 引用规则:

    • 分包不能引用其他分包的内容
    • 分包可以引用主包内容
    • 主包可以引用分包内容(通过require等方式)