Skip to content

Commit 940878b

Browse files
authored
Merge pull request #975 from imxv/fix-963-description
feat: 补充 omi-suspense 演示例子和 补充 omi-transition 演示例子
2 parents ea99b59 + ae81af4 commit 940878b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+5384
-0
lines changed

examples/suspense/.gitignore

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
10+
node_modules
11+
dist
12+
dist-ssr
13+
*.local
14+
15+
# Editor directories and files
16+
.vscode/*
17+
!.vscode/extensions.json
18+
.idea
19+
.DS_Store
20+
*.suo
21+
*.ntvs*
22+
*.njsproj
23+
*.sln
24+
*.sw?

examples/suspense/README.md

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
# Omi Suspense 演示示例
2+
3+
这个项目展示了如何使用 Omi 框架的 Suspense 组件来处理异步数据加载、组件懒加载以及错误处理。Suspense 组件让异步操作变得简单优雅,提升了用户体验。
4+
5+
## 项目简介
6+
7+
Omi Suspense 是 Omi 框架提供的一个强大组件,用于处理异步依赖。它可以:
8+
9+
- 优雅地处理异步数据加载
10+
- 支持组件懒加载
11+
- 提供加载状态和错误状态的处理
12+
- 简化异步操作的代码结构
13+
14+
本示例展示了一个用户信息加载的场景,通过 Suspense 组件处理数据获取、加载状态和错误处理。
15+
16+
## 安装步骤
17+
18+
1. 克隆项目并安装依赖:
19+
20+
```bash
21+
npm i
22+
```
23+
24+
2. 启动开发服务器:
25+
26+
```bash
27+
npm start
28+
```
29+
30+
3. 构建项目:
31+
32+
```bash
33+
npm run build
34+
```
35+
36+
## 使用说明
37+
38+
本示例展示了如何:
39+
40+
1. 使用 Suspense 组件加载异步数据
41+
2. 处理加载状态(显示加载指示器)
42+
3. 处理错误状态(显示错误信息和重试按钮)
43+
4. 在数据加载完成后更新 UI
44+
45+
## 示例代码解析
46+
47+
### 主应用组件
48+
49+
```tsx
50+
@tag('my-app')
51+
export default class extends Component {
52+
static css = [tailwind, css]
53+
54+
// 模拟异步数据获取
55+
fetchUserData = async () => {
56+
return new Promise((resolve, reject) => {
57+
setTimeout(() => {
58+
// 随机决定是否模拟错误
59+
if (Math.random() > 0.9) {
60+
reject(new Error('获取用户数据失败'))
61+
} else {
62+
resolve({
63+
name: 'Jasper',
64+
age: 28,
65+
job: '前端开发工程师'
66+
})
67+
}
68+
}, 1500) // 延迟1.5秒模拟网络请求
69+
})
70+
}
71+
72+
render() {
73+
return (
74+
<div class="container mx-auto p-4">
75+
<h1 class="text-3xl font-bold mb-8">OMI Suspense 示例</h1>
76+
77+
<o-suspense
78+
imports={[import('./components/user-info')]}
79+
data={this.fetchUserData}
80+
minLoadingTime={500}
81+
onLoaded={(event) => {
82+
userData.value = event.detail[1] // 获取data函数返回的结果
83+
}}
84+
pending={
85+
<div class="p-4 bg-blue-100 rounded animate-pulse">
86+
<p class="text-blue-800">加载用户数据中...</p>
87+
</div>
88+
}
89+
fallback={(error) => (
90+
<div class="p-4 bg-red-100 rounded">
91+
<p class="text-red-800">加载失败: {error.message}</p>
92+
<button
93+
class="mt-2 px-4 py-2 bg-red-500 text-white rounded"
94+
onClick={() => this.update()}
95+
>
96+
重试
97+
</button>
98+
</div>
99+
)}
100+
>
101+
<user-info-container>
102+
<user-profile></user-profile>
103+
</user-info-container>
104+
</o-suspense>
105+
</div>
106+
)
107+
}
108+
}
109+
```
110+
111+
### 用户信息容器组件
112+
113+
```tsx
114+
@tag('user-info-container')
115+
export default class UserInfoContainer extends Component {
116+
render() {
117+
return (
118+
<div class="bg-white p-6 rounded-lg shadow-lg mb-4">
119+
<div class="border-t border-gray-200 pt-4">
120+
<slot></slot>
121+
</div>
122+
</div>
123+
)
124+
}
125+
}
126+
```
127+
128+
### 用户资料组件
129+
130+
```tsx
131+
@tag('user-profile')
132+
class UserProfile extends Component {
133+
render() {
134+
const user = userData.value;
135+
136+
if (!user) {
137+
return <div>无用户数据</div>;
138+
}
139+
140+
return (
141+
<div class="p-4 border rounded-lg shadow-md">
142+
<h2 class="text-xl font-bold mb-2">用户信息</h2>
143+
<p>姓名: {user.name}</p>
144+
<p>年龄: {user.age}</p>
145+
<p>职业: {user.job}</p>
146+
</div>
147+
)
148+
}
149+
}
150+
```
151+
152+
## Suspense 组件的关键特性
153+
154+
### 1. 组件属性
155+
156+
| 属性 | 类型 | 描述 |
157+
|------|------|------|
158+
| `imports` | `Promise<unknown>[]` | 需要异步加载的组件模块 |
159+
| `data` | `Function` | 返回 Promise 的数据获取函数 |
160+
| `minLoadingTime` | `number` | 最小加载时间(毫秒),防止闪烁 |
161+
| `onLoaded` | `Function` | 数据加载完成的回调函数 |
162+
| `beforePending` | `Function` | 加载状态显示前的钩子函数 |
163+
| `pending` | `VNode/Function` | 加载状态的渲染内容 |
164+
| `fallback` | `VNode/Function` | 错误状态的渲染内容 |
165+
| `customRender` | `Function` | 自定义渲染函数 |
166+
167+
### 2. 事件
168+
169+
- `pending`: 开始加载时触发
170+
- `resolve`: 加载成功时触发
171+
- `loaded`: 加载完成时触发,包含加载结果
172+
- `data-loaded`: 数据加载完成时触发
173+
- `fallback`: 加载失败时触发
174+
175+
## 如何处理异步加载和错误状态
176+
177+
### 异步加载状态
178+
179+
Suspense 组件提供了两种方式来处理加载状态:
180+
181+
1. **使用 `pending` 属性**
182+
183+
```tsx
184+
<o-suspense
185+
pending={
186+
<div class="loading-indicator">
187+
<p>加载中...</p>
188+
</div>
189+
}
190+
>
191+
{/* 内容 */}
192+
</o-suspense>
193+
```
194+
195+
2. **使用命名插槽**
196+
197+
```tsx
198+
<o-suspense>
199+
<div slot="pending">加载中...</div>
200+
{/* 内容 */}
201+
</o-suspense>
202+
```
203+
204+
### 错误处理
205+
206+
同样,错误状态也有两种处理方式:
207+
208+
1. **使用 `fallback` 属性**
209+
210+
```tsx
211+
<o-suspense
212+
fallback={(error) => (
213+
<div class="error-container">
214+
<p>错误: {error.message}</p>
215+
<button onClick={() => this.update()}>重试</button>
216+
</div>
217+
)}
218+
>
219+
{/* 内容 */}
220+
</o-suspense>
221+
```
222+
223+
2. **使用命名插槽**
224+
225+
```tsx
226+
<o-suspense>
227+
<div slot="fallback">加载失败,请重试</div>
228+
{/* 内容 */}
229+
</o-suspense>
230+
```
231+
232+
### 最小加载时间
233+
234+
为了防止加载状态闪烁(特别是在网络较快的情况下),可以设置最小加载时间:
235+
236+
```tsx
237+
<o-suspense minLoadingTime={500}>
238+
{/* 内容 */}
239+
</o-suspense>
240+
```
241+
242+
### 自定义渲染
243+
244+
对于更复杂的渲染逻辑,可以使用 `customRender` 属性:
245+
246+
```tsx
247+
<o-suspense
248+
customRender={(results) => {
249+
// 根据 results 自定义渲染逻辑
250+
return <div>{/* 自定义内容 */}</div>
251+
}}
252+
>
253+
{/* 内容 */}
254+
</o-suspense>
255+
```
256+
257+
## 最佳实践
258+
259+
1. **合理设置 minLoadingTime**:避免加载状态闪烁
260+
2. **提供友好的错误处理**:显示明确的错误信息和重试选项
261+
3. **使用 beforePending 钩子**:在显示加载状态前执行过渡动画
262+
4. **合理组织组件结构**:将异步加载的组件拆分为独立模块
263+
264+
## 许可证
265+
266+
MIT © OMI

examples/suspense/index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Vite + Omi + TS</title>
8+
</head>
9+
<body>
10+
<div id="app"></div>
11+
<script type="module" src="/src/main.tsx"></script>
12+
</body>
13+
</html>

examples/suspense/package.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "omi-example-suspense",
3+
"private": true,
4+
"version": "0.0.0",
5+
"type": "module",
6+
"scripts": {
7+
"start": "vite",
8+
"dev": "vite",
9+
"build": "tsc && vite build",
10+
"preview": "vite preview"
11+
},
12+
"dependencies": {
13+
"omi": "latest",
14+
"omi-suspense": "^0.1.6"
15+
},
16+
"devDependencies": {
17+
"autoprefixer": "^10.4.16",
18+
"eslint": "^8.45.0",
19+
"postcss": "^8.4.31",
20+
"prettier": "3.0.3",
21+
"sass": "^1.55.0",
22+
"tailwindcss": "^3.3.3",
23+
"ts-node": "^10.9.1",
24+
"typescript": "^5.0.2",
25+
"vite": "^4.4.5"
26+
}
27+
}

0 commit comments

Comments
 (0)