feat: 创建 PageTransitionWrapper - 封装页面过渡动画
## 功能
创建页面过渡动画包装组件,封装复杂的嵌套逻辑
## 核心特性
### 1. 页面过渡动画
使用 framer-motion 提供流畅的页面切换动画:
- **AnimatePresence**: 管理组件进入/退出动画
- **MotionBox**: 动画化的 Box 组件
- mode="wait": 等待退出动画完成后再进入
### 2. 错误边界隔离
**ErrorBoundary 包裹**
- 隔离页面错误,确保导航栏不受影响
- 错误发生时,导航栏仍然可用
- 提供降级 UI(由 ErrorBoundary 组件处理)
### 3. 懒加载支持
**Suspense 边界**
- 支持 React.lazy() 懒加载路由组件
- 显示 PageLoader 组件作为 fallback
- 可自定义加载消息
### 4. 配置化设计
支持自定义配置:
- `animationConfig`: 自定义动画参数
- initial: 初始状态
- animate: 动画状态
- exit: 退出状态
- transition: 过渡配置
- `loaderMessage`: 自定义加载消息
### 5. React.memo 优化
使用 memo 避免不必要的重新渲染
## 封装的复杂逻辑
原 MainLayout 中 18 行复杂嵌套:
```
<Box flex="1" position="relative" overflow="hidden">
<AnimatePresence mode="wait">
<MotionBox key={location.pathname} ...>
<ErrorBoundary>
<Suspense fallback={<PageLoader />}>
<Outlet />
</Suspense>
</ErrorBoundary>
</MotionBox>
</AnimatePresence>
</Box>
```
现在简化为:
```
<PageTransitionWrapper location={location} animationConfig={...}>
<Outlet />
</PageTransitionWrapper>
```
## 优势
- ✅ 单一职责:只负责页面过渡和错误隔离
- ✅ 配置化:支持自定义动画
- ✅ 可复用:可在其他 Layout 中使用
- ✅ 可测试:独立组件,易于单元测试
- ✅ 可维护:清晰的组件边界
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
66
src/layouts/components/PageTransitionWrapper.js
Normal file
66
src/layouts/components/PageTransitionWrapper.js
Normal file
@@ -0,0 +1,66 @@
|
||||
// src/layouts/components/PageTransitionWrapper.js
|
||||
import React, { Suspense, memo } from 'react';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import ErrorBoundary from '../../components/ErrorBoundary';
|
||||
import PageLoader from '../../components/Loading/PageLoader';
|
||||
|
||||
// 创建 motion 包裹的 Box 组件
|
||||
const MotionBox = motion(Box);
|
||||
|
||||
/**
|
||||
* 页面过渡动画包裹组件
|
||||
*
|
||||
* 功能:
|
||||
* - 页面切换时的过渡动画(AnimatePresence)
|
||||
* - 错误边界隔离(ErrorBoundary)
|
||||
* - 懒加载支持(Suspense)
|
||||
*
|
||||
* 优化:
|
||||
* - ✅ 使用 memo 避免不必要的重新渲染
|
||||
* - ✅ 支持自定义动画配置
|
||||
* - ✅ 错误隔离,确保导航栏不受影响
|
||||
*
|
||||
* @param {React.ReactNode} children - 要渲染的子组件(通常是 <Outlet />)
|
||||
* @param {object} location - 路由位置对象(用于动画 key)
|
||||
* @param {object} animationConfig - 自定义动画配置
|
||||
* @param {string} loaderMessage - 加载时显示的消息
|
||||
*/
|
||||
const PageTransitionWrapper = memo(({
|
||||
children,
|
||||
location,
|
||||
animationConfig = {
|
||||
initial: { opacity: 0, y: 20 },
|
||||
animate: { opacity: 1, y: 0 },
|
||||
exit: { opacity: 0, y: -20 },
|
||||
transition: { duration: 0.2 }
|
||||
},
|
||||
loaderMessage = '页面加载中...'
|
||||
}) => {
|
||||
return (
|
||||
<Box flex="1" position="relative" overflow="hidden">
|
||||
<AnimatePresence mode="wait">
|
||||
<MotionBox
|
||||
key={location.pathname}
|
||||
initial={animationConfig.initial}
|
||||
animate={animationConfig.animate}
|
||||
exit={animationConfig.exit}
|
||||
transition={animationConfig.transition}
|
||||
style={{ height: '100%' }}
|
||||
>
|
||||
{/* 错误边界:隔离页面错误,确保导航栏仍可用 */}
|
||||
<ErrorBoundary>
|
||||
{/* Suspense:支持 React.lazy() 懒加载 */}
|
||||
<Suspense fallback={<PageLoader message={loaderMessage} />}>
|
||||
{children}
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
</MotionBox>
|
||||
</AnimatePresence>
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
|
||||
PageTransitionWrapper.displayName = 'PageTransitionWrapper';
|
||||
|
||||
export default PageTransitionWrapper;
|
||||
Reference in New Issue
Block a user