This commit is contained in:
张成
2025-11-16 09:53:24 +08:00
parent 6f4900eb0b
commit ad971796ba
8 changed files with 143 additions and 64 deletions

View File

@@ -64,9 +64,11 @@ export default defineAppConfig({
], ],
"preloadRule": { "preloadRule": {
"home_pages/index": { // 移除首屏预加载,所有子包改为按需加载,提升启动速度
"packages": ["publish_pages", 'order_pages', 'user_pages', 'other_pages'], // 如果需要,可以在 main_pages/index 加载后再预加载常用包
"network": "all" // wifi/all "main_pages/index": {
"packages": ["publish_pages"], // 只预加载最常用的发布页面
"network": "all"
} }
}, },

View File

@@ -32,9 +32,12 @@ const CancelPopup = forwardRef((props, ref) => {
show: (onAct) => { show: (onAct) => {
onFinish.current = onAct; onFinish.current = onAct;
setVisible(true); setVisible(true);
setTimeout(() => { // 使用 requestAnimationFrame 替代 setTimeout(0),性能更好
requestAnimationFrame(() => {
requestAnimationFrame(() => {
inputRef.current && inputRef.current.focus(); inputRef.current && inputRef.current.focus();
}, 0); });
});
}, },
})); }));

View File

@@ -123,6 +123,15 @@ const ListPage = () => {
isCityPickerVisible, isCityPickerVisible,
]); ]);
// 使用 ref 保存最新的状态值,避免依赖项变化导致函数重新创建
const showSearchBarRef = useRef(showSearchBar);
const isShowInputCustomerNavBarRef = useRef(isShowInputCustomerNavBar);
useEffect(() => {
showSearchBarRef.current = showSearchBar;
isShowInputCustomerNavBarRef.current = isShowInputCustomerNavBar;
}, [showSearchBar, isShowInputCustomerNavBar]);
// ScrollView 滚动处理函数 // ScrollView 滚动处理函数
const handleScrollViewScroll = useCallback( const handleScrollViewScroll = useCallback(
(e: any) => { (e: any) => {
@@ -165,13 +174,17 @@ const ListPage = () => {
const positionThreshold = 120; // 需要滚动到距离顶部120px const positionThreshold = 120; // 需要滚动到距离顶部120px
const distanceThreshold = 80; // 需要连续滚动80px才触发 const distanceThreshold = 80; // 需要连续滚动80px才触发
// 使用 ref 获取最新值,避免依赖项变化
const currentShowSearchBar = showSearchBarRef.current;
const currentIsShowInputCustomerNavBar = isShowInputCustomerNavBarRef.current;
if ( if (
newDirection === "up" && newDirection === "up" &&
currentScrollTop > positionThreshold && currentScrollTop > positionThreshold &&
totalScrollDistance > distanceThreshold totalScrollDistance > distanceThreshold
) { ) {
// 上滑超过阈值,且连续滚动距离足够,隐藏搜索框 // 上滑超过阈值,且连续滚动距离足够,隐藏搜索框
if (showSearchBar || !isShowInputCustomerNavBar) { if (currentShowSearchBar || !currentIsShowInputCustomerNavBar) {
setShowSearchBar(false); setShowSearchBar(false);
updateListPageState({ updateListPageState({
isShowInputCustomerNavBar: true, isShowInputCustomerNavBar: true,
@@ -184,7 +197,7 @@ const ListPage = () => {
currentScrollTop <= positionThreshold currentScrollTop <= positionThreshold
) { ) {
// 下滑且连续滚动距离足够,或者回到顶部附近,显示搜索框 // 下滑且连续滚动距离足够,或者回到顶部附近,显示搜索框
if (!showSearchBar || isShowInputCustomerNavBar) { if (!currentShowSearchBar || currentIsShowInputCustomerNavBar) {
setShowSearchBar(true); setShowSearchBar(true);
updateListPageState({ updateListPageState({
isShowInputCustomerNavBar: false, isShowInputCustomerNavBar: false,
@@ -197,7 +210,8 @@ const ListPage = () => {
lastScrollTopRef.current = currentScrollTop; lastScrollTopRef.current = currentScrollTop;
lastScrollTimeRef.current = currentTime; lastScrollTimeRef.current = currentTime;
}, },
[showSearchBar, isShowInputCustomerNavBar, updateListPageState] [updateListPageState]
// 移除 showSearchBar 和 isShowInputCustomerNavBar 依赖,使用 ref 获取最新值
); );
useEffect(() => { useEffect(() => {
@@ -216,7 +230,10 @@ const ListPage = () => {
isShowInputCustomerNavBar: false, isShowInputCustomerNavBar: false,
}); });
} }
}, [matches, pageOption?.page, updateListPageState]); // eslint-disable-next-line react-hooks/exhaustive-deps
}, [matches?.length, pageOption?.page]);
// 注意updateListPageState 是稳定的函数引用,不需要加入依赖项
// 只依赖实际会变化的数据matches 的长度和 pageOption.page
// 清理定时器 // 清理定时器
useEffect(() => { useEffect(() => {
@@ -280,10 +297,10 @@ const ListPage = () => {
duration: 1000, duration: 1000,
}); });
} finally { } finally {
// 使用 setTimeout 确保状态更新在下一个事件循环中执行,让 ScrollView 能正确响应 // 使用 requestAnimationFrame 替代 setTimeout(0),性能更好
setTimeout(() => { requestAnimationFrame(() => {
setRefreshing(false); setRefreshing(false);
}, 0); });
} }
}; };

View File

@@ -110,6 +110,15 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
} }
}, [scrollToTopTrigger, scrollToTopInternal]); }, [scrollToTopTrigger, scrollToTopInternal]);
// 使用 ref 保存最新的状态值,避免依赖项变化导致函数重新创建
const showSearchBarRef = useRef(showSearchBar);
const isShowInputCustomerNavBarRef = useRef(isShowInputCustomerNavBar);
useEffect(() => {
showSearchBarRef.current = showSearchBar;
isShowInputCustomerNavBarRef.current = isShowInputCustomerNavBar;
}, [showSearchBar, isShowInputCustomerNavBar]);
// ScrollView 滚动处理 // ScrollView 滚动处理
const handleScrollViewScroll = useCallback( const handleScrollViewScroll = useCallback(
(e: any) => { (e: any) => {
@@ -143,12 +152,16 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
const positionThreshold = 120; const positionThreshold = 120;
const distanceThreshold = 80; const distanceThreshold = 80;
// 使用 ref 获取最新值,避免依赖项变化
const currentShowSearchBar = showSearchBarRef.current;
const currentIsShowInputCustomerNavBar = isShowInputCustomerNavBarRef.current;
if ( if (
newDirection === "up" && newDirection === "up" &&
currentScrollTop > positionThreshold && currentScrollTop > positionThreshold &&
totalScrollDistance > distanceThreshold totalScrollDistance > distanceThreshold
) { ) {
if (showSearchBar || !isShowInputCustomerNavBar) { if (currentShowSearchBar || !currentIsShowInputCustomerNavBar) {
setShowSearchBar(false); setShowSearchBar(false);
updateListPageState({ updateListPageState({
isShowInputCustomerNavBar: true, isShowInputCustomerNavBar: true,
@@ -160,7 +173,7 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
(newDirection === "down" && totalScrollDistance > distanceThreshold) || (newDirection === "down" && totalScrollDistance > distanceThreshold) ||
currentScrollTop <= positionThreshold currentScrollTop <= positionThreshold
) { ) {
if (!showSearchBar || isShowInputCustomerNavBar) { if (!currentShowSearchBar || currentIsShowInputCustomerNavBar) {
setShowSearchBar(true); setShowSearchBar(true);
updateListPageState({ updateListPageState({
isShowInputCustomerNavBar: false, isShowInputCustomerNavBar: false,
@@ -173,12 +186,8 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
lastScrollTopRef.current = currentScrollTop; lastScrollTopRef.current = currentScrollTop;
lastScrollTimeRef.current = currentTime; lastScrollTimeRef.current = currentTime;
}, },
[ [updateListPageState, onNavStateChange]
showSearchBar, // 移除 showSearchBar 和 isShowInputCustomerNavBar 依赖,使用 ref 获取最新值
isShowInputCustomerNavBar,
updateListPageState,
onNavStateChange,
]
); );
useEffect(() => { useEffect(() => {
@@ -196,7 +205,10 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
}); });
onNavStateChange?.({ isShowInputCustomerNavBar: false }); onNavStateChange?.({ isShowInputCustomerNavBar: false });
} }
}, [matches, pageOption?.page, updateListPageState, onNavStateChange]); // eslint-disable-next-line react-hooks/exhaustive-deps
}, [matches?.length, pageOption?.page]);
// 注意updateListPageState 和 onNavStateChange 是稳定的函数引用,不需要加入依赖项
// 只依赖实际会变化的数据matches 的长度和 pageOption.page
useEffect(() => { useEffect(() => {
return () => { return () => {
@@ -236,9 +248,10 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
duration: 1000, duration: 1000,
}); });
} finally { } finally {
setTimeout(() => { // 使用 requestAnimationFrame 替代 setTimeout(0),性能更好
requestAnimationFrame(() => {
setRefreshing(false); setRefreshing(false);
}, 0); });
} }
}; };
@@ -252,13 +265,16 @@ const ListPageContent: React.FC<ListPageContentProps> = ({
// 先通知父组件筛选弹窗状态变化(设置 z-index // 先通知父组件筛选弹窗状态变化(设置 z-index
onFilterPopupVisibleChange?.(newVisible); onFilterPopupVisibleChange?.(newVisible);
// 然后更新本地状态显示/隐藏弹窗 // 然后更新本地状态显示/隐藏弹窗
// 使用 setTimeout 确保 z-index 先设置,再显示弹窗 // 使用 requestAnimationFrame 确保 z-index 先设置,再显示弹窗
if (newVisible) { if (newVisible) {
setTimeout(() => { // 使用双帧延迟确保 z-index 已生效
requestAnimationFrame(() => {
requestAnimationFrame(() => {
updateListPageState({ updateListPageState({
isShowFilterPopup: newVisible, isShowFilterPopup: newVisible,
}); });
}, 50); });
});
} else { } else {
// 关闭时直接更新状态 // 关闭时直接更新状态
updateListPageState({ updateListPageState({

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect, useCallback } from "react";
import { View, Text, Image, ScrollView } from "@tarojs/components"; import { View, Text, Image, ScrollView } from "@tarojs/components";
import Taro from "@tarojs/taro"; import Taro from "@tarojs/taro";
import "@/user_pages/myself/index.scss"; import "@/user_pages/myself/index.scss";
@@ -40,13 +40,8 @@ const MyselfPageContent: React.FC = () => {
// 确保从编辑页面返回时刷新数据 // 确保从编辑页面返回时刷新数据
}); });
useEffect(() => { // 分类球局数据(使用 useCallback 包装,避免每次渲染都创建新函数)
if (!loading) { const classifyGameRecords = useCallback((
load_game_data();
}
}, [active_tab]);
const classifyGameRecords = (
game_records: TennisMatch[] game_records: TennisMatch[]
): { notEndGames: TennisMatch[]; finishedGames: TennisMatch[] } => { ): { notEndGames: TennisMatch[]; finishedGames: TennisMatch[] } => {
const now = new Date().getTime(); const now = new Date().getTime();
@@ -64,9 +59,10 @@ const MyselfPageContent: React.FC = () => {
finishedGames: [] as TennisMatch[], finishedGames: [] as TennisMatch[],
} }
); );
}; }, []);
const load_game_data = async () => { // 使用 useCallback 包装 load_game_data避免每次渲染都创建新函数
const load_game_data = useCallback(async () => {
try { try {
if (!user_info || !("id" in user_info)) { if (!user_info || !("id" in user_info)) {
return; return;
@@ -89,7 +85,13 @@ const MyselfPageContent: React.FC = () => {
} catch (error) { } catch (error) {
console.error("加载球局数据失败:", error); console.error("加载球局数据失败:", error);
} }
}; }, [active_tab, user_info, classifyGameRecords]);
useEffect(() => {
if (!loading) {
load_game_data();
}
}, [loading, load_game_data]);
const handle_follow = async () => { const handle_follow = async () => {
try { try {

View File

@@ -38,7 +38,8 @@ const MainPage: React.FC = () => {
} }
}; };
init(); init();
}, [fetchUserInfo]); // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); // fetchUserInfo 是稳定的函数引用,不需要加入依赖项
// 处理标签切换 // 处理标签切换
const handleTabChange = useCallback((code: string) => { const handleTabChange = useCallback((code: string) => {

View File

@@ -27,13 +27,31 @@ export const useKeyboardStore = create<KeyboardStore>((set, get) => ({
setKeyboardHeight: (height: number) => { setKeyboardHeight: (height: number) => {
set({ keyboardHeight: height }) set({ keyboardHeight: height })
const { listeners } = get() const { listeners } = get()
listeners.forEach(listener => listener(height, get().isKeyboardVisible)) // 使用 requestAnimationFrame 异步执行监听器,避免阻塞主线程
requestAnimationFrame(() => {
listeners.forEach(listener => {
try {
listener(height, get().isKeyboardVisible)
} catch (error) {
console.error('键盘监听器执行错误:', error)
}
})
})
}, },
setKeyboardVisible: (visible: boolean) => { setKeyboardVisible: (visible: boolean) => {
set({ isKeyboardVisible: visible }) set({ isKeyboardVisible: visible })
const { listeners } = get() const { listeners } = get()
listeners.forEach(listener => listener(get().keyboardHeight, visible)) // 使用 requestAnimationFrame 异步执行监听器,避免阻塞主线程
requestAnimationFrame(() => {
listeners.forEach(listener => {
try {
listener(get().keyboardHeight, visible)
} catch (error) {
console.error('键盘监听器执行错误:', error)
}
})
})
}, },
addListener: (listener: (height: number, visible: boolean) => void) => { addListener: (listener: (height: number, visible: boolean) => void) => {
@@ -52,10 +70,27 @@ export const useKeyboardStore = create<KeyboardStore>((set, get) => ({
console.log('初始化全局键盘监听器') console.log('初始化全局键盘监听器')
// 使用防抖优化,避免频繁触发
let lastHeight = 0
let lastTime = 0
const debounceDelay = 16 // 约一帧的时间
Taro.onKeyboardHeightChange?.((res: any) => { Taro.onKeyboardHeightChange?.((res: any) => {
const height = Number(res?.height || 0) const height = Number(res?.height || 0)
const now = Date.now()
// 防抖:如果高度没变化或时间间隔太短,忽略
if (height === lastHeight && (now - lastTime) < debounceDelay) {
return
}
lastHeight = height
lastTime = now
console.log('全局键盘高度变化:', height) console.log('全局键盘高度变化:', height)
// 使用 requestAnimationFrame 延迟执行,避免阻塞消息处理
requestAnimationFrame(() => {
const store = get() const store = get()
if (height > 0) { if (height > 0) {
store.setKeyboardVisible(true) store.setKeyboardVisible(true)
@@ -65,6 +100,7 @@ export const useKeyboardStore = create<KeyboardStore>((set, get) => ({
store.setKeyboardHeight(0) store.setKeyboardHeight(0)
} }
}) })
})
set({ isInitialized: true }) set({ isInitialized: true })
}, },

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect, useCallback } from "react";
import { View, Text, Image, ScrollView } from "@tarojs/components"; import { View, Text, Image, ScrollView } from "@tarojs/components";
import Taro, { useDidShow } from "@tarojs/taro"; import Taro, { useDidShow } from "@tarojs/taro";
import "./index.scss"; import "./index.scss";
@@ -79,14 +79,8 @@ const MyselfPage: React.FC = () => {
// set_user_info(useUserInfo()); // 确保从编辑页面返回时刷新数据 // set_user_info(useUserInfo()); // 确保从编辑页面返回时刷新数据
}); });
// 切换标签页时重新加载球局数据 // 分类球局数据(使用 useCallback 包装,避免每次渲染都创建新函数)
useEffect(() => { const classifyGameRecords = useCallback((
if (!loading) {
load_game_data();
}
}, [active_tab]);
// 分类球局数据
const classifyGameRecords = (
game_records: TennisMatch[] game_records: TennisMatch[]
): { notEndGames: TennisMatch[]; finishedGames: TennisMatch[] } => { ): { notEndGames: TennisMatch[]; finishedGames: TennisMatch[] } => {
const now = new Date().getTime(); const now = new Date().getTime();
@@ -104,9 +98,10 @@ const MyselfPage: React.FC = () => {
finishedGames: [] as TennisMatch[], finishedGames: [] as TennisMatch[],
} }
); );
}; }, []);
// 加载球局数据
const load_game_data = async () => { // 加载球局数据(使用 useCallback 包装,避免每次渲染都创建新函数)
const load_game_data = useCallback(async () => {
try { try {
let games_data; let games_data;
if (active_tab === "hosted") { if (active_tab === "hosted") {
@@ -127,7 +122,14 @@ const MyselfPage: React.FC = () => {
} catch (error) { } catch (error) {
console.error("加载球局数据失败:", error); console.error("加载球局数据失败:", error);
} }
}; }, [active_tab, user_info, classifyGameRecords]);
// 切换标签页时重新加载球局数据
useEffect(() => {
if (!loading) {
load_game_data();
}
}, [loading, load_game_data]);
// 处理关注/取消关注 // 处理关注/取消关注
const handle_follow = async () => { const handle_follow = async () => {