React应用拉取AWS图片资源时显示全局加载屏实现方案
实现方案(React + Redux + MUI v5 + TypeScript)
核心思路为绕开单张图片绑定加载状态的实现路径,在应用层统一拦截渲染时机:所有跨页面AWS图片预加载完成前仅展示全局加载遮罩,无需修改现有页面的图片样式,适配成本为0。
1. 配置Redux全局加载状态
在Redux中新增资源加载切片,统一存储加载状态、加载进度,支持全局读取,方便后续扩展进度展示等能力。
// src/store/slices/assetLoadSlice.ts import { createSlice, PayloadAction } from '@reduxjs/toolkit'; interface AssetLoadState { isAllAwsImgsLoaded: boolean; loadProgress: number; // 取值范围0-100 } const initialState: AssetLoadState = { isAllAwsImgsLoaded: false, loadProgress: 0, }; const assetLoadSlice = createSlice({ name: 'assetLoad', initialState, reducers: { setLoadProgress: (state, action: PayloadAction<number>) => { state.loadProgress = Math.min(100, Math.max(0, action.payload)); }, setAllImgsLoaded: (state) => { state.isAllAwsImgsLoaded = true; state.loadProgress = 100; }, }, }); export const { setLoadProgress, setAllImgsLoaded } = assetLoadSlice.actions; export default assetLoadSlice.reducer;
将该reducer加入Redux store配置即可。
2. 统一维护AWS资源清单与预加载逻辑
将7个页面用到的所有AWS Bucket图片链接统一收敛到常量文件维护,避免散落在业务代码中难以统计;同时封装通用预加载方法,单张图片加载失败不阻塞全局流程,防止单图异常导致页面永久处于加载状态。
// src/constants/awsAssets.ts // 在此处补全7个页面的所有AWS图片链接 export const ALL_AWS_IMAGE_URLS: string[] = [ 'https://[你的AWS Bucket域名]/path/to/img1.png', 'https://[你的AWS Bucket域名]/path/to/img2.jpg', ]; // 单张图片预加载方法 const preloadSingleImg = (url: string): Promise<void> => { return new Promise((resolve) => { const img = new Image(); // 加载成功、失败都触发resolve,避免单图异常阻塞全局 img.onload = () => resolve(); img.onerror = () => resolve(); img.src = url; }); }; // 批量预加载,实时回调进度 export const preloadAllAwsImgs = async (onProgressUpdate: (progress: number) => void) => { const total = ALL_AWS_IMAGE_URLS.length; let loadedCount = 0; const loadTasks = ALL_AWS_IMAGE_URLS.map(async (url) => { await preloadSingleImg(url); loadedCount++; onProgressUpdate(Math.round((loadedCount / total) * 100)); }); await Promise.all(loadTasks); };
3. 根组件实现全局渲染拦截
在应用最外层根组件(通常为App.tsx,位于路由组件外层)中,应用初始化即触发全量图片预加载;加载未完成时仅渲染MUI原生组件实现的全屏加载层,加载完成后再渲染实际路由与页面内容。
// src/App.tsx import { useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { Backdrop, CircularProgress, LinearProgress, Box, Typography } from '@mui/material'; import { setLoadProgress, setAllImgsLoaded } from './store/slices/assetLoadSlice'; import { preloadAllAwsImgs } from './constants/awsAssets'; import AppRouter from './router'; // 替换为项目自身的路由配置组件 import type { RootState } from './store'; function App() { const dispatch = useDispatch(); const { isAllAwsImgsLoaded, loadProgress } = useSelector((state: RootState) => state.assetLoad); useEffect(() => { const startPreload = async () => { await preloadAllAwsImgs((progress) => { dispatch(setLoadProgress(progress)); }); dispatch(setAllImgsLoaded()); }; startPreload(); }, [dispatch]); // 资源未加载完成时,仅展示全屏加载层 if (!isAllAwsImgsLoaded) { return ( <Backdrop open sx={{ zIndex: (theme) => theme.zIndex.drawer + 999, color: '#fff', flexDirection: 'column', gap: 2 }} > <CircularProgress color="inherit" size={60} /> <Box sx={{ width: '240px' }}> <LinearProgress variant="determinate" value={loadProgress} /> </Box> <Typography variant="body2">资源加载中 {loadProgress}%</Typography> </Backdrop> ); } // 所有资源加载完成后,渲染实际页面与路由 return <AppRouter />; } export default App;
加载层直接使用MUI内置的Backdrop、进度条组件,无需额外编写自定义样式,zIndex设置为最高层级确保完全覆盖页面内容。
可选优化方向
- 若后续AWS图片资源过多、首屏预加载等待时间过长,可将全量预加载调整为路由级预加载:按路由拆分图片清单,在路由守卫中触发对应路由的图片预加载,核心逻辑与上述方案一致,仅需将预加载触发时机从应用初始化调整为路由跳转前。
- 可在加载层关闭、页面渲染时增加淡入过渡动画,优化切换体验。
- 图片资源可拼接版本hash参数,规避浏览器缓存导致的预加载资源与实际渲染资源不一致问题。
内容的提问来源于stack exchange,提问作者Alex Belz
相关产品推荐
相关产品推荐

