@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.
Files changed (48) hide show
  1. package/dist/421.js +4 -4
  2. package/dist/cli.js +74 -66
  3. package/dist/index.d.ts +1 -2
  4. package/dist/template-empty/README.md +1 -1
  5. package/dist/template-empty/package.json +2 -2
  6. package/dist/template-starter/README.md +48 -169
  7. package/dist/template-starter/package.json +2 -2
  8. package/dist/template-starter/src/app.config.ts +2 -3
  9. package/dist/template-starter/src/pages/home/index.scss +38 -172
  10. package/dist/template-starter/src/pages/home/index.tsx +20 -78
  11. package/dist/template-starter/src/widgets/hello-world/index.scss +23 -0
  12. package/dist/template-starter/src/widgets/hello-world/index.tsx +29 -0
  13. package/package.json +6 -2
  14. package/dist/template-empty/AGENTS.md +0 -299
  15. package/dist/template-empty/references/examples/common-patterns.md +0 -589
  16. package/dist/template-empty/references/examples/component-basics.md +0 -526
  17. package/dist/template-empty/references/guides/best-practices.md +0 -571
  18. package/dist/template-empty/references/guides/component-development.md +0 -891
  19. package/dist/template-empty/references/guides/performance-optimization.md +0 -402
  20. package/dist/template-empty/references/guides/troubleshooting.md +0 -287
  21. package/dist/template-empty/references/reference/components-quick-ref.md +0 -103
  22. package/dist/template-empty/references/reference/framework-api-quick-ref.md +0 -550
  23. package/dist/template-empty/references/reference/open-api/README.md +0 -8
  24. package/dist/template-empty/references/reference/open-api.md +0 -11
  25. package/dist/template-empty/references/rules/dos-and-donts.md +0 -467
  26. package/dist/template-starter/AGENTS.md +0 -299
  27. package/dist/template-starter/debug-scene/render-home-page-full.json +0 -6
  28. package/dist/template-starter/debug-scene/render-home-page.json +0 -5
  29. package/dist/template-starter/debug-scene/render-react-lynx-page-full.json +0 -6
  30. package/dist/template-starter/references/examples/common-patterns.md +0 -589
  31. package/dist/template-starter/references/examples/component-basics.md +0 -526
  32. package/dist/template-starter/references/guides/best-practices.md +0 -571
  33. package/dist/template-starter/references/guides/component-development.md +0 -891
  34. package/dist/template-starter/references/guides/performance-optimization.md +0 -402
  35. package/dist/template-starter/references/guides/troubleshooting.md +0 -287
  36. package/dist/template-starter/references/reference/components-quick-ref.md +0 -103
  37. package/dist/template-starter/references/reference/framework-api-quick-ref.md +0 -550
  38. package/dist/template-starter/references/reference/open-api/README.md +0 -8
  39. package/dist/template-starter/references/reference/open-api.md +0 -11
  40. package/dist/template-starter/references/rules/dos-and-donts.md +0 -467
  41. package/dist/template-starter/src/pages/applet/index.scss +0 -279
  42. package/dist/template-starter/src/pages/applet/index.tsx +0 -78
  43. package/dist/template-starter/src/pages/lynx/index.scss +0 -249
  44. package/dist/template-starter/src/pages/lynx/index.tsx +0 -78
  45. package/dist/template-starter/src/pages/react-lynx/index.scss +0 -335
  46. package/dist/template-starter/src/pages/react-lynx/index.tsx +0 -132
  47. package/dist/template-starter/src/widgets/weather-card/index.scss +0 -272
  48. package/dist/template-starter/src/widgets/weather-card/index.tsx +0 -193
@@ -1,891 +0,0 @@
1
- # 组件开发指南
2
-
3
- Page(页面)和 Widget(卡片)组件的完整开发指南。
4
-
5
- ---
6
-
7
- ## 🎯 Page 开发指南
8
-
9
- ### App 配置、可选入口与多级目录
10
-
11
- `src/app.config.ts` 是应用配置文件,需要配置 `appId` 和 `name`。`pages` / `widgets` 是可选的显式入口配置。
12
- 一级页面目录会自动发现,例如 `src/pages/home/index.tsx` 会产出 `pageId: 'home'`;不配置到 `pages` 时仍可构建,
13
- 但 `title`、`description` 等页面 metadata 为空。
14
-
15
- 需要补 metadata、固定首页顺序或声明多级目录时,再配置 `pages` 数组。`pages` 是有序数组,第一项会作为应用首页
16
- (最终 manifest 的 `appletEntry`);如果希望 `home` 是默认打开页面,把 `pages/home/index` 放在第一项。
17
- 数组项可以是字符串,也可以是 `{ entry, ...metadata }` 对象:只声明入口时用字符串;需要补 metadata 时用对象。
18
-
19
- 如果不配置 `pages` / `widgets`,一级 Page / Widget 仍会按目录产出;Page / Widget 的默认 ID 取入口最后一级目录名,
20
- 页面/卡片 metadata 字段为空,Widget 的 `boxType` 默认为 `inbox`。没有显式 `pages` 顺序时,首页入口会走构建兜底/历史默认
21
- `index`;首页不是 `index` 时建议配置 `pages` 第一项。
22
-
23
- 多级目录不会被一级扫描自动发现,例如 `src/pages/account/demo/index.tsx` 需要显式写
24
- `entry: 'pages/account/demo/index'`。如果不配置 `id`,默认 `pageId` 是最后一级目录名 `demo`。
25
-
26
- ```ts
27
- import { defineAppConfig } from '@doubao-apps/kit';
28
-
29
- export default defineAppConfig({
30
- appId: 'com.example.my-doubao-app',
31
- name: '我的豆包应用',
32
- pages: [
33
- 'pages/home/index',
34
- 'pages/account/demo/index',
35
- {
36
- entry: 'pages/profile/index',
37
- id: 'profile',
38
- title: '资料页',
39
- description: '补充 metadata 的页面示例'
40
- }
41
- ]
42
- });
43
- ```
44
-
45
- ### 基本结构
46
-
47
- ```tsx
48
- import { definePage, getViewData, useState, useEffect } from '@doubao-apps/framework';
49
- import './index.scss';
50
-
51
- export default definePage({
52
- // 1. 生命周期钩子
53
- onShow() {
54
- // 页面显示时调用(包括从其他页面返回)
55
- console.log('页面显示');
56
- },
57
-
58
- onHide() {
59
- // 页面隐藏时调用
60
- console.log('页面隐藏');
61
- },
62
-
63
- onDestroy() {
64
- // 页面销毁时调用
65
- console.log('页面销毁');
66
- },
67
-
68
- // 2. 渲染函数
69
- render() {
70
- // 使用 getViewData() 和 getViewContext() 获取数据和上下文
71
- const viewData = getViewData();
72
-
73
- return <MyPageComponent {...viewData} />;
74
- }
75
- });
76
- ```
77
-
78
- ### 页面布局示例
79
-
80
- **1. 默认页面布局**
81
-
82
- ```tsx
83
- export default definePage({
84
- render() {
85
- return (
86
- <view className="full-page">
87
- <view className="header">头部</view>
88
- <view className="content">内容区域</view>
89
- <view className="footer">底部</view>
90
- </view>
91
- );
92
- }
93
- });
94
- ```
95
-
96
- **2. 九宫格布局示例**
97
-
98
- ```tsx
99
- export default definePage({
100
- render() {
101
- const items = [
102
- { id: '1', title: '应用1', icon: '🎯' },
103
- { id: '2', title: '应用2', icon: '📱' },
104
- { id: '3', title: '应用3', icon: '⚡' }
105
- ];
106
-
107
- return (
108
- <view className="grid-container">
109
- {items.map(item => (
110
- <view key={item.id} className="grid-item">
111
- <text className="icon">{item.icon}</text>
112
- <text className="title">{item.title}</text>
113
- </view>
114
- ))}
115
- </view>
116
- );
117
- }
118
- });
119
- ```
120
-
121
- ### 页面参数接收
122
-
123
- ```tsx
124
- interface PageViewData {
125
- userId: string;
126
- tab?: string;
127
- }
128
-
129
- export default definePage({
130
- render() {
131
- const viewData = getViewData<PageViewData>();
132
- const { userId, tab = 'profile' } = viewData;
133
-
134
- return (
135
- <view>
136
- <text>用户 ID: {userId}</text>
137
- <text>当前标签: {tab}</text>
138
- </view>
139
- );
140
- }
141
- });
142
- ```
143
-
144
- ### 页面导航
145
-
146
- ```tsx
147
- import { navigateTo, redirectTo, reLaunch } from '@doubao-apps/framework/api';
148
-
149
- // 保留当前页面,打开新页面
150
- navigateTo({
151
- url: 'user-detail?userId=123&tab=profile'
152
- });
153
-
154
- // 替换当前页面
155
- redirectTo({
156
- url: 'settings'
157
- });
158
-
159
- // 关闭所有页面并回到首页
160
- reLaunch({
161
- url: 'home'
162
- });
163
- ```
164
-
165
- `url` 在常规页面跳转里填写页面的 `pageId`。显式配置时取 `src/app.config.ts` 中对应 page 的 `id`;不配置时取入口目录名,例如
166
- `src/pages/detail/index.tsx` 默认为 `detail`。
167
-
168
- 页面内可继续通过 `getViewData<PageViewData>()` 读取 URL 中传入的参数。
169
-
170
- ### 常用模式
171
-
172
- **1. 数据加载**
173
-
174
- ```tsx
175
- import { useEffect, useState } from '@doubao-apps/framework';
176
- import { request } from '@doubao-apps/framework/api';
177
-
178
- function MyPage({ userId }: { userId: string }) {
179
- const [data, setData] = useState(null);
180
- const [loading, setLoading] = useState(true);
181
- const [error, setError] = useState(null);
182
-
183
- useEffect(() => {
184
- const fetchData = async () => {
185
- try {
186
- setLoading(true);
187
- const result = await request({
188
- url: `/api/users/${userId}`,
189
- method: 'GET'
190
- });
191
- setData(result.data);
192
- } catch (err) {
193
- setError(err.message);
194
- } finally {
195
- setLoading(false);
196
- }
197
- };
198
-
199
- fetchData();
200
- }, [userId]);
201
-
202
- if (loading) return <text>加载中...</text>;
203
- if (error) return <text>错误: {error}</text>;
204
- if (!data) return <text>无数据</text>;
205
-
206
- return <view>{/* 渲染数据 */}</view>;
207
- }
208
- ```
209
-
210
- **2. 表单提交**
211
-
212
- ```tsx
213
- import { useState } from '@doubao-apps/framework';
214
- import { navigateBack, request, showToast } from '@doubao-apps/framework/api';
215
-
216
- function FormPage() {
217
- const [formData, setFormData] = useState({
218
- name: '',
219
- email: ''
220
- });
221
- const [submitting, setSubmitting] = useState(false);
222
-
223
- const handleSubmit = async () => {
224
- try {
225
- setSubmitting(true);
226
- await request({
227
- url: '/api/submit',
228
- method: 'POST',
229
- data: formData
230
- });
231
- showToast({ message: '提交成功' });
232
- navigateBack(); // 返回上一页
233
- } catch (err) {
234
- showToast({ message: '提交失败' });
235
- } finally {
236
- setSubmitting(false);
237
- }
238
- };
239
-
240
- return (
241
- <view>
242
- <input
243
- value={formData.name}
244
- onInput={e => setFormData({ ...formData, name: e.detail.value })}
245
- placeholder="姓名"
246
- />
247
- <input
248
- value={formData.email}
249
- onInput={e => setFormData({ ...formData, email: e.detail.value })}
250
- placeholder="邮箱"
251
- />
252
- <button onClick={handleSubmit} disabled={submitting}>
253
- {submitting ? '提交中...' : '提交'}
254
- </button>
255
- </view>
256
- );
257
- }
258
- ```
259
-
260
- ---
261
-
262
- ## 🎴 Widget 开发指南
263
-
264
- ### 可选 Widgets 配置
265
-
266
- 一级 Widget 目录会自动发现,例如 `src/widgets/my-widget/index.tsx` 会产出 `widgetId: 'my-widget'`。不配置
267
- `widgets` 时仍可构建,但 `name`、`description` 等 metadata 为空,`boxType` 默认是 `inbox`。
268
-
269
- 需要补 metadata 或声明多级目录时,再配置 `widgets` 数组。多级目录如 `src/widgets/order/detail/index.tsx`
270
- 需要显式写 `entry: 'widgets/order/detail/index'`;如果不配置 `id`,默认 `widgetId` 是最后一级目录名 `detail`。
271
- 数组项可以是字符串,也可以是 `{ entry, ...metadata }` 对象:只声明入口时用字符串;需要补 metadata 时用对象。
272
-
273
- ```ts
274
- import { defineAppConfig } from '@doubao-apps/kit';
275
-
276
- export default defineAppConfig({
277
- appId: 'com.example.my-doubao-app',
278
- name: '我的豆包应用',
279
- widgets: [
280
- 'widgets/simple-card/index',
281
- 'widgets/order/detail/index',
282
- {
283
- entry: 'widgets/my-widget/index',
284
- id: 'my-widget', // 卡片唯一标识
285
- name: '我的卡片', // 卡片名称
286
- description: '卡片功能描述', // 卡片描述
287
- boxType: 'inbox', // 卡片类型: inbox | full_box
288
- border: true,
289
- keywords: ['demo', 'widget'],
290
- titleType: 'normal'
291
- }
292
- ]
293
- });
294
- ```
295
-
296
- ### 基本结构
297
-
298
- ```tsx
299
- import { defineWidget, getViewData } from '@doubao-apps/framework';
300
- import './index.scss';
301
-
302
- interface MyWidgetData {
303
- title: string;
304
- content?: string;
305
- }
306
-
307
- export default defineWidget({
308
- onShow() {
309
- console.log('卡片显示');
310
- },
311
-
312
- onHide() {
313
- console.log('卡片隐藏');
314
- },
315
-
316
- onForeground() {
317
- console.log('应用回到前台');
318
- },
319
-
320
- onBackground() {
321
- console.log('应用进入后台');
322
- },
323
-
324
- onMounted() {
325
- // 卡片挂载时调用
326
- console.log('卡片挂载');
327
- },
328
-
329
- onDestroy() {
330
- console.log('卡片销毁');
331
- },
332
-
333
- render() {
334
- const viewData = getViewData<MyWidgetData>();
335
- return <MyWidgetComponent {...viewData} />;
336
- }
337
- });
338
- ```
339
-
340
- ### 卡片类型 (boxType)
341
-
342
- **1. inbox - 普通卡片**
343
-
344
- 默认卡片类型,适用于绝大多数场景(信息展示、结果回显、轻量交互)。
345
-
346
- 推荐使用场景:
347
- - 内容相对简短:标题/摘要/列表/状态提示等
348
- - 不需要占用整行宽度,希望保持对话流的紧凑阅读体验
349
- - 交互以按钮、简单表单项为主
350
-
351
- ```ts
352
- // src/app.config.ts
353
- import { defineAppConfig } from '@doubao-apps/kit';
354
-
355
- export default defineAppConfig({
356
- appId: 'com.example.my-doubao-app',
357
- name: '我的豆包应用',
358
- widgets: [
359
- {
360
- entry: 'widgets/info-card/index',
361
- id: 'info-card',
362
- name: '信息卡片',
363
- boxType: 'inbox'
364
- }
365
- ]
366
- });
367
- ```
368
-
369
- ```tsx
370
- export default defineWidget({
371
- render() {
372
- const viewData = getViewData<{ title: string; content: string }>();
373
-
374
- return (
375
- <view className="info-card">
376
- <text className="title">{viewData.title}</text>
377
- <text className="content">{viewData.content}</text>
378
- </view>
379
- );
380
- }
381
- });
382
- ```
383
-
384
- **2. full_box - 全宽卡片**
385
-
386
- 适用于需要更大展示空间的卡片(例如图表、图片墙、复杂表单等)。
387
-
388
- ```ts
389
- // src/app.config.ts
390
- import { defineAppConfig } from '@doubao-apps/kit';
391
-
392
- export default defineAppConfig({
393
- appId: 'com.example.my-doubao-app',
394
- name: '我的豆包应用',
395
- widgets: [
396
- {
397
- entry: 'widgets/user-action-card/index',
398
- id: 'user-action-card',
399
- name: '全宽卡片示例',
400
- boxType: 'full_box'
401
- }
402
- ]
403
- });
404
- ```
405
-
406
- ```tsx
407
- export default defineWidget({
408
- render() {
409
- const viewData = getViewData<{ action: string; result: string }>();
410
- return (
411
- <view className="user-action-card">
412
- <text>操作: {viewData.action}</text>
413
- <text>结果: {viewData.result}</text>
414
- </view>
415
- );
416
- }
417
- });
418
- ```
419
-
420
- ### ViewData 类型
421
-
422
- Widget viewData 通过 `getViewData<T>()` 的泛型参数和 TypeScript 接口表达。
423
-
424
- ```tsx
425
- interface ProductCardData {
426
- name: string;
427
- price: number;
428
- images?: string[];
429
- category: '电子' | '服装' | '食品' | '图书';
430
- inStock: boolean;
431
- specs?: {
432
- color?: string;
433
- size?: string;
434
- };
435
- }
436
-
437
- export default defineWidget({
438
- render() {
439
- const viewData = getViewData<ProductCardData>();
440
- return (
441
- <view className="product-card">
442
- <text className="name">{viewData.name}</text>
443
- <text className="price">¥{viewData.price}</text>
444
- {viewData.images && (
445
- <view className="images">
446
- {viewData.images.map((img, idx) => (
447
- <image key={idx} src={img} />
448
- ))}
449
- </view>
450
- )}
451
- <text className="category">{viewData.category}</text>
452
- {!viewData.inStock && <text className="out-of-stock">缺货</text>}
453
- </view>
454
- );
455
- }
456
- });
457
- ```
458
-
459
- ### 常用模式
460
-
461
- **1. 数据获取和展示**
462
-
463
- ```tsx
464
- import { useEffect, useState } from '@doubao-apps/framework';
465
- import { request } from '@doubao-apps/framework/api';
466
-
467
- function WeatherWidget({ city }: { city: string }) {
468
- const [weather, setWeather] = useState(null);
469
- const [loading, setLoading] = useState(true);
470
-
471
- useEffect(() => {
472
- const fetchWeather = async () => {
473
- try {
474
- const response = await request({
475
- url: `/api/weather?city=${city}`,
476
- method: 'GET'
477
- });
478
- setWeather(response.data);
479
- } catch (err) {
480
- console.error('获取天气失败:', err);
481
- } finally {
482
- setLoading(false);
483
- }
484
- };
485
-
486
- fetchWeather();
487
- }, [city]);
488
-
489
- if (loading) return <text>加载中...</text>;
490
- if (!weather) return <text>无数据</text>;
491
-
492
- return (
493
- <view className="weather-widget">
494
- <text className="city">{weather.city}</text>
495
- <text className="temp">{weather.temperature}°C</text>
496
- <text className="condition">{weather.condition}</text>
497
- </view>
498
- );
499
- }
500
- ```
501
-
502
- **2. 用户交互**
503
-
504
- ```tsx
505
- import { sendQueryMessage } from '@doubao-apps/framework/api';
506
-
507
- function InteractiveWidget({ initialCount }: { initialCount: number }) {
508
- const [count, setCount] = useState(initialCount);
509
-
510
- const handleIncrement = () => {
511
- setCount(count + 1);
512
- // 发送一条用户消息到当前会话
513
- sendQueryMessage({
514
- content: `计数增加到 ${count + 1}`,
515
- type: 'text'
516
- });
517
- };
518
-
519
- return (
520
- <view className="interactive-widget">
521
- <text>当前计数: {count}</text>
522
- <button onClick={handleIncrement}>增加</button>
523
- </view>
524
- );
525
- }
526
- ```
527
-
528
- **3. 打开页面**
529
-
530
- ```tsx
531
- import { navigateTo } from '@doubao-apps/framework/api';
532
-
533
- function ActionWidget({ itemId }: { itemId: string }) {
534
- const handleViewDetail = () => {
535
- navigateTo({
536
- url: `item-detail?id=${itemId}`
537
- });
538
- };
539
-
540
- return (
541
- <view>
542
- <button onClick={handleViewDetail}>查看详情</button>
543
- </view>
544
- );
545
- }
546
- ```
547
-
548
- ---
549
-
550
- ## 🎨 样式开发
551
-
552
- ### SCSS 文件组织
553
-
554
- ```scss
555
- // 样式变量
556
- $primary-color: #1890ff;
557
- $text-color: #333;
558
- $bg-color: #f5f5f5;
559
- $border-radius: 8px;
560
- $spacing: 16px;
561
-
562
- // 容器样式
563
- .container {
564
- padding: $spacing;
565
- background-color: $bg-color;
566
-
567
- .header {
568
- font-size: 36px;
569
- color: $text-color;
570
- margin-bottom: $spacing;
571
- }
572
-
573
- .content {
574
- background-color: #fff;
575
- border-radius: $border-radius;
576
- padding: $spacing;
577
- }
578
- }
579
-
580
- // 响应式布局
581
- .responsive-layout {
582
- display: flex;
583
- flex-wrap: wrap;
584
- gap: $spacing;
585
-
586
- .item {
587
- flex: 1 1 calc(50% - #{$spacing});
588
- min-width: 200px;
589
- }
590
- }
591
- ```
592
-
593
- ### 常用布局模式
594
-
595
- **1. Flex 布局**
596
-
597
- ```scss
598
- .flex-container {
599
- display: flex;
600
- flex-direction: row; // row | column
601
- justify-content: center; // flex-start | center | flex-end | space-between
602
- align-items: center; // flex-start | center | flex-end | stretch
603
- gap: 16px;
604
- }
605
- ```
606
-
607
- **2. Grid 布局**
608
-
609
- ```scss
610
- .grid-container {
611
- display: grid;
612
- grid-template-columns: repeat(3, 1fr);
613
- gap: 16px;
614
- padding: 16px;
615
- }
616
- ```
617
-
618
- **3. 卡片布局**
619
-
620
- ```scss
621
- .card {
622
- background-color: #fff;
623
- border-radius: 8px;
624
- padding: 24px;
625
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
626
- margin-bottom: 16px;
627
- }
628
- ```
629
-
630
- ---
631
-
632
- ## 🔌 View Context APIs
633
-
634
- 在 Page 和 Widget 开发中,有时需要访问视图的上下文信息和数据。Framework 提供了两个核心 API:
635
-
636
- ### `getViewContext()`
637
-
638
- 获取当前视图的上下文信息,包含页面环境、配置和语言设置。
639
-
640
- **类型定义**:
641
- ```typescript
642
- import type { AIViewContext } from '@doubao-apps/framework';
643
- import { getViewContext } from '@doubao-apps/framework';
644
-
645
- // AIViewContext 的字段以实际类型定义为准,这里仅展示常用字段:
646
- // - viewId / pageName / lang / appSettings / replyFor
647
- ```
648
-
649
- **使用场景**:
650
-
651
- 1. **多语言支持**:
652
- ```tsx
653
- import { getViewContext } from '@doubao-apps/framework';
654
-
655
- function GreetingWidget() {
656
- const context = getViewContext();
657
-
658
- const greeting = context.lang === 'zh-CN' ? '你好' : 'Hello';
659
-
660
- return <text>{greeting}!</text>;
661
- }
662
- ```
663
-
664
- 2. **访问应用配置**:
665
- ```tsx
666
- function ThemeAwareComponent() {
667
- const context = getViewContext();
668
- const theme = context.appSettings.theme || 'light';
669
-
670
- return <view className={`theme-${theme}`}>...</view>;
671
- }
672
- ```
673
-
674
- 3. **调试和日志**:
675
- ```tsx
676
- function MyPage() {
677
- const context = getViewContext();
678
-
679
- useEffect(() => {
680
- console.log('当前页面:', context.pageName);
681
- console.log('View ID:', context.viewId);
682
- }, []);
683
-
684
- return <view>...</view>;
685
- }
686
- ```
687
-
688
- ### `getViewData<T>()`
689
-
690
- 获取当前视图的数据,支持泛型类型定义,确保类型安全。
691
-
692
- **类型定义**:
693
- ```typescript
694
- import { getViewData } from '@doubao-apps/framework';
695
-
696
- function getViewData<T extends Record<string, any>>(): T
697
- ```
698
-
699
- **使用场景**:
700
-
701
- 1. **在非 render 函数中获取数据**:
702
- ```tsx
703
- import { defineWidget, getViewData } from '@doubao-apps/framework';
704
-
705
- interface TodoData {
706
- tasks: string[];
707
- }
708
-
709
- export default defineWidget({
710
- onMounted() {
711
- // 在生命周期钩子中访问数据
712
- const viewData = getViewData<TodoData>();
713
- console.log('任务列表:', viewData.tasks);
714
- },
715
-
716
- render() {
717
- // render 函数中使用 getViewData() 获取数据
718
- const viewData = getViewData<TodoData>();
719
- return <view>{viewData.tasks.length} 个任务</view>;
720
- }
721
- });
722
- ```
723
-
724
- 2. **在自定义函数中访问数据**:
725
- ```tsx
726
- interface WeatherData {
727
- city: string;
728
- temperature: number;
729
- }
730
-
731
- function WeatherWidget() {
732
- const loadWeatherIcon = () => {
733
- const viewData = getViewData<WeatherData>();
734
- return viewData.temperature > 25 ? '☀️' : '❄️';
735
- };
736
-
737
- return (
738
- <view>
739
- <text>{loadWeatherIcon()}</text>
740
- </view>
741
- );
742
- }
743
- ```
744
-
745
- **注意事项**:
746
- - ✅ **推荐**:在 render 函数中使用 `getViewData()` 获取数据
747
- - ✅ **推荐**:在生命周期钩子和自定义函数中使用 `getViewData()` 获取数据
748
- - 🔒 **类型安全**:始终定义明确的 TypeScript 接口并传递给泛型参数
749
-
750
- ### 使用 `getViewData()` 和 `getViewContext()`
751
-
752
- render 函数不再通过参数接收 viewData 和 context,请使用以下方法获取数据和上下文:
753
-
754
- **推荐做法**:
755
- ```tsx
756
- // ✅ 推荐:render 函数中使用 getViewData()
757
- render() {
758
- const viewData = getViewData<WeatherData>();
759
- return <text>{viewData.city}: {viewData.temperature}°C</text>;
760
- }
761
-
762
- // ✅ 推荐:生命周期钩子中使用 getViewData()
763
- onMounted() {
764
- const viewData = getViewData<WeatherData>();
765
- // 处理数据...
766
- }
767
-
768
- // ✅ 推荐:同时获取数据和上下文
769
- render() {
770
- const viewData = getViewData<WeatherData>();
771
- const context = getViewContext();
772
- return <text>{viewData.city} - {context.lang}</text>;
773
- }
774
- ```
775
-
776
- ---
777
-
778
- ## 🔧 开发最佳实践
779
-
780
- ### 1. TypeScript 类型定义
781
-
782
- ```tsx
783
- // 定义 props 类型
784
- interface ComponentProps {
785
- title: string;
786
- count?: number;
787
- onAction: (value: string) => void;
788
- }
789
-
790
- // 定义 state 类型
791
- interface ComponentState {
792
- loading: boolean;
793
- data: DataType | null;
794
- error: string | null;
795
- }
796
-
797
- function MyComponent({ title, count = 0, onAction }: ComponentProps) {
798
- const [state, setState] = useState<ComponentState>({
799
- loading: false,
800
- data: null,
801
- error: null
802
- });
803
-
804
- return <view>{/* 组件内容 */}</view>;
805
- }
806
- ```
807
-
808
- ### 2. 错误处理
809
-
810
- ```tsx
811
- try {
812
- // 异步操作
813
- const result = await someAsyncOperation();
814
- setData(result);
815
- } catch (err) {
816
- // 记录错误
817
- console.error('操作失败:', err);
818
-
819
- // 显示用户友好的错误消息
820
- setError('操作失败,请重试');
821
-
822
- // 可选:上报错误
823
- reportAppLog({
824
- level: 'error',
825
- message: err.message,
826
- context: { operation: 'someAsyncOperation' }
827
- });
828
- }
829
- ```
830
-
831
- ### 3. 性能优化
832
-
833
- ```tsx
834
- import { useMemo, useCallback } from '@doubao-apps/framework';
835
-
836
- function OptimizedComponent({ data, onSelect }) {
837
- // 缓存计算结果
838
- const processedData = useMemo(() => {
839
- return data.map(item => ({
840
- ...item,
841
- computed: expensiveComputation(item)
842
- }));
843
- }, [data]);
844
-
845
- // 缓存回调函数
846
- const handleClick = useCallback((id: string) => {
847
- onSelect(id);
848
- }, [onSelect]);
849
-
850
- return (
851
- <view>
852
- {processedData.map(item => (
853
- <view key={item.id} onClick={() => handleClick(item.id)}>
854
- {item.computed}
855
- </view>
856
- ))}
857
- </view>
858
- );
859
- }
860
- ```
861
-
862
- ### 4. 生命周期管理
863
-
864
- ```tsx
865
- useEffect(() => {
866
- // 订阅
867
- const subscription = subscribeEvent('data-update', handleDataUpdate);
868
-
869
- // 定时器
870
- const timer = setInterval(() => {
871
- fetchData();
872
- }, 5000);
873
-
874
- // 清理函数
875
- return () => {
876
- unsubscribeEvent(subscription);
877
- clearInterval(timer);
878
- };
879
- }, []);
880
- ```
881
-
882
- ---
883
-
884
- ## 📚 延伸阅读
885
-
886
- - **组件示例** → [../examples/component-basics.md](../examples/component-basics.md)
887
- - **常用模式** → [../examples/common-patterns.md](../examples/common-patterns.md)
888
- - **最佳实践** → [best-practices.md](./best-practices.md)
889
- - **性能优化** → [performance-optimization.md](./performance-optimization.md)
890
- - **故障排查** → [troubleshooting.md](./troubleshooting.md)
891
- - **API 参考** → [../reference/framework-api-quick-ref.md](../reference/framework-api-quick-ref.md)