@doubao-apps/create 0.0.33 → 0.0.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/421.js +4 -4
- package/dist/cli.js +74 -66
- package/dist/index.d.ts +1 -2
- package/dist/template-empty/README.md +1 -1
- package/dist/template-empty/package.json +2 -2
- package/dist/template-starter/README.md +48 -169
- package/dist/template-starter/package.json +2 -2
- package/dist/template-starter/src/app.config.ts +2 -3
- package/dist/template-starter/src/pages/home/index.scss +38 -172
- package/dist/template-starter/src/pages/home/index.tsx +20 -78
- package/dist/template-starter/src/widgets/hello-world/index.scss +23 -0
- package/dist/template-starter/src/widgets/hello-world/index.tsx +29 -0
- package/package.json +6 -2
- package/dist/template-empty/AGENTS.md +0 -299
- package/dist/template-empty/references/examples/common-patterns.md +0 -589
- package/dist/template-empty/references/examples/component-basics.md +0 -526
- package/dist/template-empty/references/guides/best-practices.md +0 -571
- package/dist/template-empty/references/guides/component-development.md +0 -891
- package/dist/template-empty/references/guides/performance-optimization.md +0 -402
- package/dist/template-empty/references/guides/troubleshooting.md +0 -287
- package/dist/template-empty/references/reference/components-quick-ref.md +0 -103
- package/dist/template-empty/references/reference/framework-api-quick-ref.md +0 -550
- package/dist/template-empty/references/reference/open-api/README.md +0 -8
- package/dist/template-empty/references/reference/open-api.md +0 -11
- package/dist/template-empty/references/rules/dos-and-donts.md +0 -467
- package/dist/template-starter/AGENTS.md +0 -299
- package/dist/template-starter/debug-scene/render-home-page-full.json +0 -6
- package/dist/template-starter/debug-scene/render-home-page.json +0 -5
- package/dist/template-starter/debug-scene/render-react-lynx-page-full.json +0 -6
- package/dist/template-starter/references/examples/common-patterns.md +0 -589
- package/dist/template-starter/references/examples/component-basics.md +0 -526
- package/dist/template-starter/references/guides/best-practices.md +0 -571
- package/dist/template-starter/references/guides/component-development.md +0 -891
- package/dist/template-starter/references/guides/performance-optimization.md +0 -402
- package/dist/template-starter/references/guides/troubleshooting.md +0 -287
- package/dist/template-starter/references/reference/components-quick-ref.md +0 -103
- package/dist/template-starter/references/reference/framework-api-quick-ref.md +0 -550
- package/dist/template-starter/references/reference/open-api/README.md +0 -8
- package/dist/template-starter/references/reference/open-api.md +0 -11
- package/dist/template-starter/references/rules/dos-and-donts.md +0 -467
- package/dist/template-starter/src/pages/applet/index.scss +0 -279
- package/dist/template-starter/src/pages/applet/index.tsx +0 -78
- package/dist/template-starter/src/pages/lynx/index.scss +0 -249
- package/dist/template-starter/src/pages/lynx/index.tsx +0 -78
- package/dist/template-starter/src/pages/react-lynx/index.scss +0 -335
- package/dist/template-starter/src/pages/react-lynx/index.tsx +0 -132
- package/dist/template-starter/src/widgets/weather-card/index.scss +0 -272
- package/dist/template-starter/src/widgets/weather-card/index.tsx +0 -193
|
@@ -1,402 +0,0 @@
|
|
|
1
|
-
# 性能优化
|
|
2
|
-
|
|
3
|
-
Doubao Apps SDK 应用的性能优化指南和最佳实践。
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## ⚡ 渲染性能优化
|
|
8
|
-
|
|
9
|
-
### 1. 避免不必要的渲染
|
|
10
|
-
|
|
11
|
-
#### 使用 useMemo 缓存计算结果
|
|
12
|
-
|
|
13
|
-
```tsx
|
|
14
|
-
// ✅ 好的做法 - 缓存昂贵的计算
|
|
15
|
-
function DataList({ items, filter }: Props) {
|
|
16
|
-
// 只在 items 或 filter 改变时重新计算
|
|
17
|
-
const filteredAndSorted = useMemo(() => {
|
|
18
|
-
console.log('Computing filtered list...');
|
|
19
|
-
return items
|
|
20
|
-
.filter(item => item.type === filter)
|
|
21
|
-
.sort((a, b) => b.score - a.score);
|
|
22
|
-
}, [items, filter]);
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
<list>
|
|
26
|
-
{filteredAndSorted.map(item => (
|
|
27
|
-
<ItemCard key={item.id} {...item} />
|
|
28
|
-
))}
|
|
29
|
-
</list>
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// ❌ 不好的做法 - 每次渲染都重新计算
|
|
34
|
-
function DataList({ items, filter }: Props) {
|
|
35
|
-
// 每次组件渲染都会执行
|
|
36
|
-
const filteredAndSorted = items
|
|
37
|
-
.filter(item => item.type === filter)
|
|
38
|
-
.sort((a, b) => b.score - a.score);
|
|
39
|
-
|
|
40
|
-
return <list>{/* ... */}</list>;
|
|
41
|
-
}
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
#### 使用 useCallback 稳定函数引用
|
|
45
|
-
|
|
46
|
-
```tsx
|
|
47
|
-
// ✅ 好的做法 - 稳定的回调函数
|
|
48
|
-
function ParentComponent({ items }: Props) {
|
|
49
|
-
const [selectedId, setSelectedId] = useState<string | null>(null);
|
|
50
|
-
|
|
51
|
-
// 函数引用保持稳定,子组件不会因此重新渲染
|
|
52
|
-
const handleSelect = useCallback((id: string) => {
|
|
53
|
-
setSelectedId(id);
|
|
54
|
-
console.log('Selected:', id);
|
|
55
|
-
}, []);
|
|
56
|
-
|
|
57
|
-
return (
|
|
58
|
-
<view>
|
|
59
|
-
{items.map(item => (
|
|
60
|
-
<ItemCard
|
|
61
|
-
key={item.id}
|
|
62
|
-
item={item}
|
|
63
|
-
onSelect={handleSelect} // 函数引用不变
|
|
64
|
-
/>
|
|
65
|
-
))}
|
|
66
|
-
</view>
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// ❌ 不好的做法 - 每次渲染创建新函数
|
|
71
|
-
function ParentComponent({ items }: Props) {
|
|
72
|
-
const [selectedId, setSelectedId] = useState<string | null>(null);
|
|
73
|
-
|
|
74
|
-
return (
|
|
75
|
-
<view>
|
|
76
|
-
{items.map(item => (
|
|
77
|
-
<ItemCard
|
|
78
|
-
key={item.id}
|
|
79
|
-
item={item}
|
|
80
|
-
onSelect={(id) => setSelectedId(id)} // 每次创建新函数
|
|
81
|
-
/>
|
|
82
|
-
))}
|
|
83
|
-
</view>
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
### 2. 列表渲染优化
|
|
89
|
-
|
|
90
|
-
#### 虚拟列表
|
|
91
|
-
|
|
92
|
-
```tsx
|
|
93
|
-
// ✅ 好的做法 - 使用 Lynx list 组件的虚拟滚动
|
|
94
|
-
function VirtualList({ items }: { items: Item[] }) {
|
|
95
|
-
return (
|
|
96
|
-
<list
|
|
97
|
-
className="virtual-list"
|
|
98
|
-
list-type="single"
|
|
99
|
-
span-count={1}
|
|
100
|
-
scroll-orientation="vertical"
|
|
101
|
-
enable-nested-scroll={true}
|
|
102
|
-
// Lynx 会自动复用不可见的列表项
|
|
103
|
-
>
|
|
104
|
-
{items.map(item => (
|
|
105
|
-
<list-item key={item.id} item-key={item.id}>
|
|
106
|
-
<ItemCard item={item} />
|
|
107
|
-
</list-item>
|
|
108
|
-
))}
|
|
109
|
-
</list>
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// ❌ 不好的做法 - 渲染所有项
|
|
114
|
-
function AllItemsList({ items }: { items: Item[] }) {
|
|
115
|
-
// 如果有 1000+ 项,会严重影响性能
|
|
116
|
-
return (
|
|
117
|
-
<scroll-view>
|
|
118
|
-
{items.map(item => (
|
|
119
|
-
<ItemCard key={item.id} item={item} />
|
|
120
|
-
))}
|
|
121
|
-
</scroll-view>
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
#### 分页加载
|
|
127
|
-
|
|
128
|
-
```tsx
|
|
129
|
-
// ✅ 好的做法 - 分页加载大量数据
|
|
130
|
-
function InfiniteList() {
|
|
131
|
-
const [items, setItems] = useState<Item[]>([]);
|
|
132
|
-
const [page, setPage] = useState(1);
|
|
133
|
-
const [hasMore, setHasMore] = useState(true);
|
|
134
|
-
const [loading, setLoading] = useState(false);
|
|
135
|
-
|
|
136
|
-
const loadMore = async () => {
|
|
137
|
-
if (loading || !hasMore) return;
|
|
138
|
-
|
|
139
|
-
setLoading(true);
|
|
140
|
-
try {
|
|
141
|
-
const newItems = await fetchItems(page, 20);
|
|
142
|
-
setItems(prev => [...prev, ...newItems]);
|
|
143
|
-
setPage(prev => prev + 1);
|
|
144
|
-
setHasMore(newItems.length === 20);
|
|
145
|
-
} finally {
|
|
146
|
-
setLoading(false);
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
return (
|
|
151
|
-
<list onScrollToLower={loadMore}>
|
|
152
|
-
{items.map(item => (
|
|
153
|
-
<list-item key={item.id}>
|
|
154
|
-
<ItemCard item={item} />
|
|
155
|
-
</list-item>
|
|
156
|
-
))}
|
|
157
|
-
{loading && <LoadingIndicator />}
|
|
158
|
-
</list>
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
### 3. 图片优化
|
|
164
|
-
|
|
165
|
-
#### 懒加载
|
|
166
|
-
|
|
167
|
-
```tsx
|
|
168
|
-
// ✅ 好的做法 - 图片懒加载
|
|
169
|
-
function LazyImage({ src, placeholder }: Props) {
|
|
170
|
-
const [loaded, setLoaded] = useState(false);
|
|
171
|
-
|
|
172
|
-
return (
|
|
173
|
-
<view className="lazy-image">
|
|
174
|
-
{!loaded && <image src={placeholder} className="lazy-image__placeholder" />}
|
|
175
|
-
<image
|
|
176
|
-
src={src}
|
|
177
|
-
className="lazy-image__actual"
|
|
178
|
-
style={{ display: loaded ? 'block' : 'none' }}
|
|
179
|
-
onLoad={() => setLoaded(true)}
|
|
180
|
-
onError={() => console.error('Failed to load image')}
|
|
181
|
-
/>
|
|
182
|
-
</view>
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
#### 图片尺寸优化
|
|
188
|
-
|
|
189
|
-
```tsx
|
|
190
|
-
// ✅ 好的做法 - 使用合适尺寸的图片
|
|
191
|
-
interface ImageProps {
|
|
192
|
-
src: string;
|
|
193
|
-
size: 'thumbnail' | 'medium' | 'large';
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
function OptimizedImage({ src, size }: ImageProps) {
|
|
197
|
-
const sizeParams = {
|
|
198
|
-
thumbnail: '?w=200&h=200&q=80',
|
|
199
|
-
medium: '?w=600&h=600&q=85',
|
|
200
|
-
large: '?w=1200&h=1200&q=90'
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
return <image src={`${src}${sizeParams[size]}`} />;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// ❌ 不好的做法 - 加载原图后缩小
|
|
207
|
-
function UnoptimizedImage({ src }: Props) {
|
|
208
|
-
// 加载 5MB 的原图,然后显示为 100x100
|
|
209
|
-
return <image src={src} style={{ width: '100rpx', height: '100rpx' }} />;
|
|
210
|
-
}
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
---
|
|
214
|
-
|
|
215
|
-
## 🧠 内存优化
|
|
216
|
-
|
|
217
|
-
### 1. 及时清理资源
|
|
218
|
-
|
|
219
|
-
```tsx
|
|
220
|
-
// ✅ 好的做法 - 清理定时器和订阅
|
|
221
|
-
function TimerComponent() {
|
|
222
|
-
const [count, setCount] = useState(0);
|
|
223
|
-
const timerRef = useRef<any>(null);
|
|
224
|
-
|
|
225
|
-
useEffect(() => {
|
|
226
|
-
// 创建定时器
|
|
227
|
-
timerRef.current = setInterval(() => {
|
|
228
|
-
setCount(c => c + 1);
|
|
229
|
-
}, 1000);
|
|
230
|
-
|
|
231
|
-
// 清理函数
|
|
232
|
-
return () => {
|
|
233
|
-
if (timerRef.current) {
|
|
234
|
-
clearInterval(timerRef.current);
|
|
235
|
-
timerRef.current = null;
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
}, []);
|
|
239
|
-
|
|
240
|
-
return <text>Count: {count}</text>;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// ❌ 不好的做法 - 忘记清理
|
|
244
|
-
function TimerComponent() {
|
|
245
|
-
const [count, setCount] = useState(0);
|
|
246
|
-
|
|
247
|
-
useEffect(() => {
|
|
248
|
-
setInterval(() => {
|
|
249
|
-
setCount(c => c + 1);
|
|
250
|
-
}, 1000);
|
|
251
|
-
// 没有清理!组件卸载后定时器仍在运行
|
|
252
|
-
}, []);
|
|
253
|
-
|
|
254
|
-
return <text>Count: {count}</text>;
|
|
255
|
-
}
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
### 2. 避免内存泄漏
|
|
259
|
-
|
|
260
|
-
```tsx
|
|
261
|
-
// ✅ 好的做法 - 检查组件是否已卸载
|
|
262
|
-
function DataFetcher() {
|
|
263
|
-
const [data, setData] = useState(null);
|
|
264
|
-
const mountedRef = useRef(true);
|
|
265
|
-
|
|
266
|
-
useEffect(() => {
|
|
267
|
-
const fetchData = async () => {
|
|
268
|
-
try {
|
|
269
|
-
const result = await api.getData();
|
|
270
|
-
// 只在组件仍挂载时更新状态
|
|
271
|
-
if (mountedRef.current) {
|
|
272
|
-
setData(result);
|
|
273
|
-
}
|
|
274
|
-
} catch (error) {
|
|
275
|
-
console.error(error);
|
|
276
|
-
}
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
fetchData();
|
|
280
|
-
|
|
281
|
-
return () => {
|
|
282
|
-
mountedRef.current = false;
|
|
283
|
-
};
|
|
284
|
-
}, []);
|
|
285
|
-
|
|
286
|
-
return <view>{/* render data */}</view>;
|
|
287
|
-
}
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
### 3. 大数据处理
|
|
291
|
-
|
|
292
|
-
```tsx
|
|
293
|
-
// ✅ 好的做法 - 分批处理大数据
|
|
294
|
-
async function processBigData(data: any[]) {
|
|
295
|
-
const BATCH_SIZE = 100;
|
|
296
|
-
const results = [];
|
|
297
|
-
|
|
298
|
-
for (let i = 0; i < data.length; i += BATCH_SIZE) {
|
|
299
|
-
const batch = data.slice(i, i + BATCH_SIZE);
|
|
300
|
-
const processed = await processBatch(batch);
|
|
301
|
-
results.push(...processed);
|
|
302
|
-
|
|
303
|
-
// 让出主线程,避免阻塞 UI
|
|
304
|
-
await new Promise(resolve => setTimeout(resolve, 0));
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return results;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// ❌ 不好的做法 - 一次性处理所有数据
|
|
311
|
-
async function processBigData(data: any[]) {
|
|
312
|
-
// 如果数据量大,会阻塞 UI
|
|
313
|
-
return data.map(item => expensiveOperation(item));
|
|
314
|
-
}
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
---
|
|
318
|
-
|
|
319
|
-
## 📦 包体积优化
|
|
320
|
-
|
|
321
|
-
### 1. 代码分割
|
|
322
|
-
|
|
323
|
-
```tsx
|
|
324
|
-
// ✅ 好的做法 - 动态导入大组件
|
|
325
|
-
function HeavyFeature() {
|
|
326
|
-
const [Component, setComponent] = useState<any>(null);
|
|
327
|
-
|
|
328
|
-
useEffect(() => {
|
|
329
|
-
// 只在需要时加载
|
|
330
|
-
import('./HeavyComponent').then(module => {
|
|
331
|
-
setComponent(() => module.default);
|
|
332
|
-
});
|
|
333
|
-
}, []);
|
|
334
|
-
|
|
335
|
-
if (!Component) {
|
|
336
|
-
return <LoadingSpinner />;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
return <Component />;
|
|
340
|
-
}
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
## 📱 启动性能优化
|
|
345
|
-
|
|
346
|
-
### 1. 延迟非关键代码
|
|
347
|
-
|
|
348
|
-
```tsx
|
|
349
|
-
// ✅ 好的做法 - 优先渲染关键内容
|
|
350
|
-
export default definePage({
|
|
351
|
-
onShow() {
|
|
352
|
-
// 先显示页面骨架
|
|
353
|
-
this.showSkeleton = true;
|
|
354
|
-
|
|
355
|
-
// 延迟加载非关键数据
|
|
356
|
-
setTimeout(() => {
|
|
357
|
-
this.loadAnalytics();
|
|
358
|
-
this.loadRecommendations();
|
|
359
|
-
}, 1000);
|
|
360
|
-
|
|
361
|
-
// 优先加载关键数据
|
|
362
|
-
this.loadMainContent();
|
|
363
|
-
},
|
|
364
|
-
|
|
365
|
-
render() {
|
|
366
|
-
return this.showSkeleton ? <Skeleton /> : <Content />;
|
|
367
|
-
}
|
|
368
|
-
});
|
|
369
|
-
```
|
|
370
|
-
|
|
371
|
-
### 2. 预加载关键资源
|
|
372
|
-
|
|
373
|
-
```tsx
|
|
374
|
-
// ✅ 好的做法 - 预加载下一页可能需要的数据
|
|
375
|
-
function ListPage({ items }: Props) {
|
|
376
|
-
const [nextPageData, setNextPageData] = useState<Item[]>([]);
|
|
377
|
-
|
|
378
|
-
useEffect(() => {
|
|
379
|
-
// 预加载下一页数据
|
|
380
|
-
const prefetchNextPage = async () => {
|
|
381
|
-
try {
|
|
382
|
-
const data = await fetchItems(2);
|
|
383
|
-
setNextPageData(data);
|
|
384
|
-
} catch (error) {
|
|
385
|
-
console.error('Prefetch failed:', error);
|
|
386
|
-
}
|
|
387
|
-
};
|
|
388
|
-
|
|
389
|
-
// 在用户可能操作前预加载
|
|
390
|
-
const timer = setTimeout(prefetchNextPage, 2000);
|
|
391
|
-
return () => clearTimeout(timer);
|
|
392
|
-
}, []);
|
|
393
|
-
|
|
394
|
-
return <ItemList items={items} />;
|
|
395
|
-
}
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
## 🔗 相关文档
|
|
399
|
-
|
|
400
|
-
- [最佳实践](./best-practices.md) - 代码规范
|
|
401
|
-
- [组件开发完整指南](./component-development.md) - Page 和 Widget 开发
|
|
402
|
-
- [故障排查](./troubleshooting.md) - 常见问题
|
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
# 故障排查指南
|
|
2
|
-
|
|
3
|
-
Doubao Apps SDK 开发中常见问题的诊断和解决方案。 迭代过程中的新问题建议随时添加到该文档,方便团队内共享
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## 🔍 问题诊断流程
|
|
8
|
-
|
|
9
|
-
### 系统化排查步骤
|
|
10
|
-
|
|
11
|
-
1. **复现问题** - 确定问题的触发条件
|
|
12
|
-
2. **查看日志** - 检查控制台错误信息
|
|
13
|
-
3. **隔离问题** - 缩小问题范围
|
|
14
|
-
4. **查找文档** - 搜索相关文档和示例
|
|
15
|
-
5. **尝试方案** - 逐一测试可能的解决方案
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## 🐛 常见错误类型
|
|
20
|
-
|
|
21
|
-
### 1. 组件渲染错误
|
|
22
|
-
|
|
23
|
-
#### 问题:页面白屏或组件不显示
|
|
24
|
-
|
|
25
|
-
**症状**:
|
|
26
|
-
```
|
|
27
|
-
页面显示空白,控制台无错误
|
|
28
|
-
或者提示:Cannot read property 'xxx' of undefined
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
**可能原因**:
|
|
32
|
-
- 数据未加载完成就尝试渲染
|
|
33
|
-
- 必需的 props 未传递
|
|
34
|
-
- 条件渲染逻辑错误
|
|
35
|
-
|
|
36
|
-
**解决方案**:
|
|
37
|
-
|
|
38
|
-
```tsx
|
|
39
|
-
// ❌ 错误写法
|
|
40
|
-
function UserProfile({ user }: Props) {
|
|
41
|
-
return (
|
|
42
|
-
<view>
|
|
43
|
-
<text>{user.name}</text> {/* user 可能为 null */}
|
|
44
|
-
</view>
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// ✅ 正确写法 - 添加安全检查
|
|
49
|
-
function UserProfile({ user }: Props) {
|
|
50
|
-
if (!user) {
|
|
51
|
-
return <LoadingView />;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return (
|
|
55
|
-
<view>
|
|
56
|
-
<text>{user.name}</text>
|
|
57
|
-
</view>
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// ✅ 或使用可选链
|
|
62
|
-
function UserProfile({ user }: Props) {
|
|
63
|
-
return (
|
|
64
|
-
<view>
|
|
65
|
-
<text>{user?.name || '未知用户'}</text>
|
|
66
|
-
</view>
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
#### 问题:组件频繁重新渲染
|
|
72
|
-
|
|
73
|
-
**症状**:
|
|
74
|
-
```
|
|
75
|
-
控制台大量渲染日志
|
|
76
|
-
页面卡顿,性能下降
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
**诊断方法**:
|
|
80
|
-
|
|
81
|
-
```tsx
|
|
82
|
-
// 添加渲染追踪
|
|
83
|
-
function MyComponent({ data }: Props) {
|
|
84
|
-
const renderCount = useRef(0);
|
|
85
|
-
|
|
86
|
-
useEffect(() => {
|
|
87
|
-
renderCount.current += 1;
|
|
88
|
-
console.log(`Render count: ${renderCount.current}`);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
return <view>{/* ... */}</view>;
|
|
92
|
-
}
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
**解决方案**:
|
|
96
|
-
|
|
97
|
-
```tsx
|
|
98
|
-
// ✅ 使用 useMemo 缓存计算
|
|
99
|
-
const processedData = useMemo(() => {
|
|
100
|
-
return heavyComputation(data);
|
|
101
|
-
}, [data]);
|
|
102
|
-
|
|
103
|
-
// ✅ 使用 useCallback 稳定函数引用
|
|
104
|
-
const handleClick = useCallback(() => {
|
|
105
|
-
console.log('clicked');
|
|
106
|
-
}, []);
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### 2. 生命周期问题
|
|
110
|
-
|
|
111
|
-
#### 问题:onShow 未触发
|
|
112
|
-
|
|
113
|
-
**可能原因**:
|
|
114
|
-
- 没有正确定义生命周期方法
|
|
115
|
-
- 页面未正确注册
|
|
116
|
-
- 路由配置错误
|
|
117
|
-
|
|
118
|
-
**解决方案**:
|
|
119
|
-
|
|
120
|
-
```tsx
|
|
121
|
-
// ❌ 错误写法 - 没有在正确位置定义
|
|
122
|
-
export default definePage({
|
|
123
|
-
render() {
|
|
124
|
-
// ❌ 不能在这里定义
|
|
125
|
-
const onShow = () => {
|
|
126
|
-
console.log('show');
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
return <view>...</view>;
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
// ✅ 正确写法
|
|
134
|
-
export default definePage({
|
|
135
|
-
onShow() {
|
|
136
|
-
console.log('Page shown');
|
|
137
|
-
},
|
|
138
|
-
|
|
139
|
-
onHide() {
|
|
140
|
-
console.log('Page hidden');
|
|
141
|
-
},
|
|
142
|
-
|
|
143
|
-
render() {
|
|
144
|
-
return <view>...</view>;
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
#### 问题:内存泄漏 - 组件卸载后仍在更新状态
|
|
150
|
-
|
|
151
|
-
**症状**:
|
|
152
|
-
```
|
|
153
|
-
Warning: Can't perform a React state update on an unmounted component
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
**解决方案**:
|
|
157
|
-
|
|
158
|
-
```tsx
|
|
159
|
-
// ✅ 使用 cleanup 函数
|
|
160
|
-
function DataFetcher() {
|
|
161
|
-
const [data, setData] = useState(null);
|
|
162
|
-
|
|
163
|
-
useEffect(() => {
|
|
164
|
-
let mounted = true;
|
|
165
|
-
|
|
166
|
-
const fetchData = async () => {
|
|
167
|
-
const result = await api.getData();
|
|
168
|
-
if (mounted) { // 检查组件是否仍挂载
|
|
169
|
-
setData(result);
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
fetchData();
|
|
174
|
-
|
|
175
|
-
return () => {
|
|
176
|
-
mounted = false; // cleanup
|
|
177
|
-
};
|
|
178
|
-
}, []);
|
|
179
|
-
|
|
180
|
-
return <view>{/* ... */}</view>;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// ✅ 清理定时器
|
|
184
|
-
useEffect(() => {
|
|
185
|
-
const timer = setInterval(() => {
|
|
186
|
-
console.log('tick');
|
|
187
|
-
}, 1000);
|
|
188
|
-
|
|
189
|
-
return () => {
|
|
190
|
-
clearInterval(timer); // cleanup
|
|
191
|
-
};
|
|
192
|
-
}, []);
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
### 3. 样式问题
|
|
196
|
-
|
|
197
|
-
#### 问题:样式不生效
|
|
198
|
-
|
|
199
|
-
**可能原因**:
|
|
200
|
-
- 样式文件未导入
|
|
201
|
-
- 选择器权重不够
|
|
202
|
-
- Lynx 不支持的 CSS 属性
|
|
203
|
-
- rpx 单位使用错误
|
|
204
|
-
|
|
205
|
-
**解决方案**:
|
|
206
|
-
|
|
207
|
-
```tsx
|
|
208
|
-
// ✅ 确保导入样式文件
|
|
209
|
-
import './index.scss'; // 必须导入
|
|
210
|
-
|
|
211
|
-
export default definePage({
|
|
212
|
-
// ...
|
|
213
|
-
});
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
```scss
|
|
217
|
-
// ✅ 检查选择器
|
|
218
|
-
.my-component {
|
|
219
|
-
padding: 32rpx; // ✅ 使用 rpx
|
|
220
|
-
color: #333;
|
|
221
|
-
|
|
222
|
-
&__title {
|
|
223
|
-
font-size: 36rpx;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// ❌ 避免使用不支持的属性
|
|
228
|
-
.my-component {
|
|
229
|
-
position: sticky; // ❌ Lynx 可能不支持
|
|
230
|
-
backdrop-filter: blur(10px); // ❌ 不支持
|
|
231
|
-
}
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
### 4. 数据请求问题
|
|
235
|
-
|
|
236
|
-
#### 问题:接口调用失败
|
|
237
|
-
|
|
238
|
-
**症状**:
|
|
239
|
-
```
|
|
240
|
-
Network Error
|
|
241
|
-
Request failed with status 400/401/404/500
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
**诊断步骤**:
|
|
245
|
-
|
|
246
|
-
```tsx
|
|
247
|
-
// ✅ 添加详细的错误日志
|
|
248
|
-
import { request } from '@doubao-apps/framework/api';
|
|
249
|
-
|
|
250
|
-
async function fetchData() {
|
|
251
|
-
try {
|
|
252
|
-
const response = await request({
|
|
253
|
-
url: 'https://api.example.com/data',
|
|
254
|
-
method: 'GET'
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
console.log('Response status:', response.statusCode);
|
|
258
|
-
console.log('Response headers:', response.header);
|
|
259
|
-
|
|
260
|
-
if (response.statusCode >= 400) {
|
|
261
|
-
console.error('Error response:', response.data);
|
|
262
|
-
throw new Error(`HTTP ${response.statusCode}: ${JSON.stringify(response.data)}`);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
return response.data;
|
|
266
|
-
} catch (error) {
|
|
267
|
-
console.error('Request error:', error);
|
|
268
|
-
throw error;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
## 🔧 常见解决方案速查
|
|
276
|
-
|
|
277
|
-
### 快速修复方案
|
|
278
|
-
|
|
279
|
-
| 问题 | 快速解决方案 |
|
|
280
|
-
|-----|-------------|
|
|
281
|
-
| 组件不显示 | 检查数据加载和空值处理 |
|
|
282
|
-
| 样式不生效 | 确认导入样式文件 |
|
|
283
|
-
| 内存泄漏 | 添加 cleanup 函数 |
|
|
284
|
-
| 网络请求失败 | 检查 URL、参数和错误处理 |
|
|
285
|
-
| 类型错误 | 添加可选链或类型守卫 |
|
|
286
|
-
| 页面卡顿 | 使用虚拟列表和分页 |
|
|
287
|
-
| 图片加载慢 | 使用懒加载和合适尺寸 |
|