@buaa_smat/hometrans 0.1.0 → 0.1.2

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 (136) hide show
  1. package/README.md +141 -124
  2. package/agents/build-fixer.md +1 -0
  3. package/agents/code-review-fix.md +1 -0
  4. package/agents/code-reviewer.md +1 -0
  5. package/agents/logic-coding.md +1 -0
  6. package/agents/logic-context-builder.md +1 -0
  7. package/agents/review-fixer.md +1 -0
  8. package/agents/self-test-fixer.md +1 -0
  9. package/agents/self-tester.md +260 -233
  10. package/agents/spec-generator.md +1 -0
  11. package/agents/test-tools/autotest/README.md +223 -0
  12. package/agents/test-tools/autotest/config.yaml.example +58 -0
  13. package/agents/test-tools/autotest/pyproject.toml +16 -0
  14. package/agents/test-tools/autotest/report_tool.py +759 -0
  15. package/agents/test-tools/autotest/self_test_runner.py +773 -0
  16. package/agents/test-tools/autotest/testcases_schema.md +143 -0
  17. package/agents/test-tools/autotest/testcases_tool.py +215 -0
  18. package/agents/test-tools/autotest/uv.lock +3156 -0
  19. package/agents/test-tools/harmony_autotest-0.1.0-py3-none-any.whl +0 -0
  20. package/agents/test-tools/hypium-6.1.0.210-py3-none-any.whl +0 -0
  21. package/agents/test-tools/hypium_mcp-0.6.5-py3-none-any.whl +0 -0
  22. package/agents/test-tools/xdevice-6.1.0.210-py3-none-any.whl +0 -0
  23. package/agents/test-tools/xdevice_devicetest-6.1.0.210-py3-none-any.whl +0 -0
  24. package/agents/test-tools/xdevice_ohos-6.1.0.210-py3-none-any.whl +0 -0
  25. package/dist/cli/config-store.js +27 -2
  26. package/dist/cli/config.js +17 -6
  27. package/dist/cli/index.js +3 -2
  28. package/dist/cli/init.js +135 -22
  29. package/dist/cli/mcp.js +2 -2
  30. package/dist/context/index.js +165 -69
  31. package/package.json +59 -60
  32. package/skills/code-dev-review-fix/SKILL.md +279 -0
  33. package/skills/code-dev-review-fix-workspace/evals/evals.json +56 -0
  34. package/skills/code-dev-review-fix-workspace/iteration-1/routing-results.md +23 -0
  35. package/skills/convert_pipeline/SKILL.md +423 -439
  36. package/skills/hmos-resources-convert/SKILL.md +623 -0
  37. package/skills/hmos-resources-convert/evals/evals.json +171 -0
  38. package/skills/hmos-resources-convert/references/conversion-rules.md +663 -0
  39. package/skills/hmos-resources-convert/references/dependency-analysis-rules.md +388 -0
  40. package/skills/hmos-resources-convert/references/resource-mapping-rules.md +457 -0
  41. package/skills/hmos-resources-convert/references/xml-drawable-to-svg-rules.md +513 -0
  42. package/skills/hmos-resources-convert/template/AppScope/app.json5 +10 -0
  43. package/skills/hmos-resources-convert/template/AppScope/resources/base/element/string.json +8 -0
  44. package/skills/hmos-resources-convert/template/AppScope/resources/base/media/background.png +0 -0
  45. package/skills/hmos-resources-convert/template/AppScope/resources/base/media/foreground.png +0 -0
  46. package/skills/hmos-resources-convert/template/AppScope/resources/base/media/layered_image.json +7 -0
  47. package/skills/hmos-resources-convert/template/build-profile.json5 +42 -0
  48. package/skills/hmos-resources-convert/template/code-linter.json5 +32 -0
  49. package/skills/hmos-resources-convert/template/entry/build-profile.json5 +33 -0
  50. package/skills/hmos-resources-convert/template/entry/hvigorfile.ts +6 -0
  51. package/skills/hmos-resources-convert/template/entry/obfuscation-rules.txt +23 -0
  52. package/skills/hmos-resources-convert/template/entry/oh-package.json5 +10 -0
  53. package/skills/hmos-resources-convert/template/entry/src/main/ets/entryability/EntryAbility.ets +48 -0
  54. package/skills/hmos-resources-convert/template/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets +16 -0
  55. package/skills/hmos-resources-convert/template/entry/src/main/ets/pages/Index.ets +23 -0
  56. package/skills/hmos-resources-convert/template/entry/src/main/module.json5 +55 -0
  57. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/element/color.json +8 -0
  58. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/element/float.json +8 -0
  59. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/element/string.json +16 -0
  60. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/background.png +0 -0
  61. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/foreground.png +0 -0
  62. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/layered_image.json +7 -0
  63. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/startIcon.png +0 -0
  64. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/profile/backup_config.json +3 -0
  65. package/skills/hmos-resources-convert/template/entry/src/main/resources/base/profile/main_pages.json +5 -0
  66. package/skills/hmos-resources-convert/template/entry/src/main/resources/dark/element/color.json +8 -0
  67. package/skills/hmos-resources-convert/template/entry/src/mock/mock-config.json5 +2 -0
  68. package/skills/hmos-resources-convert/template/entry/src/ohosTest/ets/test/Ability.test.ets +35 -0
  69. package/skills/hmos-resources-convert/template/entry/src/ohosTest/ets/test/List.test.ets +5 -0
  70. package/skills/hmos-resources-convert/template/entry/src/ohosTest/module.json5 +16 -0
  71. package/skills/hmos-resources-convert/template/entry/src/test/List.test.ets +5 -0
  72. package/skills/hmos-resources-convert/template/entry/src/test/LocalUnit.test.ets +33 -0
  73. package/skills/hmos-resources-convert/template/hvigor/hvigor-config.json5 +23 -0
  74. package/skills/hmos-resources-convert/template/hvigorfile.ts +6 -0
  75. package/skills/hmos-resources-convert/template/oh-package-lock.json5 +28 -0
  76. package/skills/hmos-resources-convert/template/oh-package.json5 +10 -0
  77. package/skills/hmos-resources-convert/tools/apktool.bat +85 -0
  78. package/skills/hmos-resources-convert/tools/apktool_3.0.1.jar +0 -0
  79. package/skills/hmos-ui-align/SKILL.md +182 -0
  80. package/skills/hmos-ui-align/config-example.json +11 -0
  81. package/skills/hmos-ui-align/config.json +11 -0
  82. package/skills/hmos-ui-align/diff_analysis.md +53 -0
  83. package/skills/hmos-ui-align/page_align.md +62 -0
  84. package/skills/hmos-ui-align/readme.md +231 -0
  85. package/skills/hmos-ui-align/references/Comparison_Template.md +2 -0
  86. package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Link/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/217/214/345/220/221/345/220/214/346/255/245.md +648 -0
  87. package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Observed/350/243/205/351/245/260/345/231/250/345/222/214@ObjectLink/350/243/205/351/245/260/345/231/250/357/274/232/345/265/214/345/245/227/347/261/273/345/257/271/350/261/241/345/261/236/346/200/247/345/217/230/345/214/226.md +2089 -0
  88. package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Prop/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/215/225/345/220/221/345/220/214/346/255/245.md +1033 -0
  89. package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Provide/350/243/205/351/245/260/345/231/250/345/222/214@Consume/350/243/205/351/245/260/345/231/250/357/274/232/344/270/216/345/220/216/344/273/243/347/273/204/344/273/266/345/217/214/345/220/221/345/220/214/346/255/245.md +1183 -0
  90. package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@State/350/243/205/351/245/260/345/231/250/357/274/232/347/273/204/344/273/266/345/206/205/347/212/266/346/200/201.md +576 -0
  91. package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Track/350/243/205/351/245/260/345/231/250/357/274/232class/345/257/271/350/261/241/345/261/236/346/200/247/347/272/247/346/233/264/346/226/260.md +297 -0
  92. package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Watch/350/243/205/351/245/260/345/231/250/357/274/232/347/212/266/346/200/201/345/217/230/351/207/217/346/233/264/346/224/271/351/200/232/347/237/245.md +395 -0
  93. package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/AppStorage/357/274/232/345/272/224/347/224/250/345/205/250/345/261/200/347/232/204UI/347/212/266/346/200/201/345/255/230/345/202/250.md +903 -0
  94. package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/Environment/357/274/232/350/256/276/345/244/207/347/216/257/345/242/203/346/237/245/350/257/242.md +106 -0
  95. package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/LocalStorage/357/274/232/351/241/265/351/235/242/347/272/247UI/347/212/266/346/200/201/345/255/230/345/202/250.md +1178 -0
  96. package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/MVVM/346/250/241/345/274/217V1.md +911 -0
  97. package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/MVVM/346/250/241/345/274/217/357/274/210V1/357/274/211.md +911 -0
  98. package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/PersistentStorage/357/274/232/346/214/201/344/271/205/345/214/226/345/255/230/345/202/250UI/347/212/266/346/200/201.md +355 -0
  99. package/skills/hmos-ui-align/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243//347/256/241/347/220/206/345/272/224/347/224/250/346/213/245/346/234/211/347/232/204/347/212/266/346/200/201/346/246/202/350/277/260.md +11 -0
  100. package/skills/hmos-ui-align/references/UI_Analysis_Template.md +4 -0
  101. package/skills/hmos-ui-align/references/android-to-harmonyOS-ui-atomic-component-mapping-reference.md +2535 -0
  102. package/skills/hmos-ui-align/references/android-to-harmonyOS-ui-interaction-mapping-reference.md +555 -0
  103. package/skills/hmos-ui-align/references/android-to-harmonyOS-ui-layout-mapping-reference.md +117 -0
  104. package/skills/hmos-ui-align/scripts/app_feature_verify.py +443 -0
  105. package/skills/hmos-ui-align/scripts/navigation-capure.md +37 -0
  106. package/skills/hmos-ui-align/scripts/page_capture.py +592 -0
  107. package/skills/hmos-ui-align-batch/SKILL.md +99 -0
  108. package/skills/hmos-ui-align-batch/references/conversion-procedure.md +180 -0
  109. package/skills/hmos-ui-align-batch/references/mappings/android-to-harmonyOS-ui-atomic-component-mapping-reference.md +2535 -0
  110. package/skills/hmos-ui-align-batch/references/mappings/android-to-harmonyOS-ui-interaction-mapping-reference.md +555 -0
  111. package/skills/hmos-ui-align-batch/references/mappings/android-to-harmonyOS-ui-layout-mapping-reference.md +117 -0
  112. package/skills/hmos-ui-align-batch/references/mvvm/@Link/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/217/214/345/220/221/345/220/214/346/255/245.md +648 -0
  113. package/skills/hmos-ui-align-batch/references/mvvm/@Observed/350/243/205/351/245/260/345/231/250/345/222/214@ObjectLink/350/243/205/351/245/260/345/231/250/357/274/232/345/265/214/345/245/227/347/261/273/345/257/271/350/261/241/345/261/236/346/200/247/345/217/230/345/214/226.md +2089 -0
  114. package/skills/hmos-ui-align-batch/references/mvvm/@Prop/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/215/225/345/220/221/345/220/214/346/255/245.md +1033 -0
  115. package/skills/hmos-ui-align-batch/references/mvvm/@Provide/350/243/205/351/245/260/345/231/250/345/222/214@Consume/350/243/205/351/245/260/345/231/250/357/274/232/344/270/216/345/220/216/344/273/243/347/273/204/344/273/266/345/217/214/345/220/221/345/220/214/346/255/245.md +1183 -0
  116. package/skills/hmos-ui-align-batch/references/mvvm/@State/350/243/205/351/245/260/345/231/250/357/274/232/347/273/204/344/273/266/345/206/205/347/212/266/346/200/201.md +576 -0
  117. package/skills/hmos-ui-align-batch/references/mvvm/@Track/350/243/205/351/245/260/345/231/250/357/274/232class/345/257/271/350/261/241/345/261/236/346/200/247/347/272/247/346/233/264/346/226/260.md +297 -0
  118. package/skills/hmos-ui-align-batch/references/mvvm/@Watch/350/243/205/351/245/260/345/231/250/357/274/232/347/212/266/346/200/201/345/217/230/351/207/217/346/233/264/346/224/271/351/200/232/347/237/245.md +395 -0
  119. package/skills/hmos-ui-align-batch/references/mvvm/AppStorage/357/274/232/345/272/224/347/224/250/345/205/250/345/261/200/347/232/204UI/347/212/266/346/200/201/345/255/230/345/202/250.md +903 -0
  120. package/skills/hmos-ui-align-batch/references/mvvm/Environment/357/274/232/350/256/276/345/244/207/347/216/257/345/242/203/346/237/245/350/257/242.md +106 -0
  121. package/skills/hmos-ui-align-batch/references/mvvm/LocalStorage/357/274/232/351/241/265/351/235/242/347/272/247UI/347/212/266/346/200/201/345/255/230/345/202/250.md +1178 -0
  122. package/skills/hmos-ui-align-batch/references/mvvm/MVVM/346/250/241/345/274/217/357/274/210V1/357/274/211.md +911 -0
  123. package/skills/hmos-ui-align-batch/references/mvvm/PersistentStorage/357/274/232/346/214/201/344/271/205/345/214/226/345/255/230/345/202/250UI/347/212/266/346/200/201.md +355 -0
  124. package/skills/hmos-ui-align-batch/references/mvvm//347/256/241/347/220/206/345/272/224/347/224/250/346/213/245/346/234/211/347/232/204/347/212/266/346/200/201/346/246/202/350/277/260.md +11 -0
  125. package/skills/hmos-ui-align-batch/scripts/android_parse_fast.py +1606 -0
  126. package/skills/self-test/SKILL.md +369 -0
  127. package/skills/self-test/readme.md +309 -0
  128. package/skills/spec-generator-skill/SKILL.md +332 -0
  129. package/skills/spec-generator-skill/references/android-platform-tokens.md +105 -0
  130. package/skills/spec-generator-skill/references/spec-sample-1.md +78 -0
  131. package/skills/spec-generator-skill/references/spec-sample-2.md +58 -0
  132. package/skills/spec-generator-skill/references/spec-sample-3.md +116 -0
  133. package/skills/spec-generator-skill/references/step4-report-template.md +33 -0
  134. package/agents/self-test-setup.md +0 -165
  135. package/dist/context/resources/sdkConfig.json +0 -24
  136. package/src/context/resources/sdkConfig.json +0 -24
@@ -0,0 +1,1183 @@
1
+ # @Provide装饰器和@Consume装饰器:与后代组件双向同步
2
+
3
+ @Provide和@Consume,应用于与后代组件的双向数据同步、状态数据在多个层级之间传递的场景。不同于上文提到的父子组件之间通过命名参数机制传递,@Provide和@Consume摆脱参数传递机制的束缚,实现跨层级传递。
4
+
5
+ 其中@Provide装饰的变量是在祖先组件中,可以理解为被"提供"给后代的状态变量。@Consume装饰的变量是在后代组件中,去"消费(绑定)"祖先组件提供的变量。
6
+
7
+ @Provide/@Consume是跨组件层级的双向同步。在阅读@Provide和@Consume文档前,建议开发者对UI范式基本语法和自定义组件有基本的了解。建议提前阅读:[基本语法概述](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-basic-syntax-overview),[声明式UI描述](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-declarative-ui-description),[创建自定义组件](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-create-custom-components)。最佳实践请参考[状态管理最佳实践](https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-status-management)。常见问题请参考[状态管理常见问题](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-state-management-faq)。
8
+
9
+ > **说明:**
10
+ >
11
+ > 从API version 9开始,这两个装饰器支持在ArkTS卡片中使用。
12
+ >
13
+ > 从API version 11开始,这两个装饰器支持在元服务中使用。
14
+ >
15
+ > API version 19及以前,@Provide和@Consume双向同步仅支持声明式节点场景。
16
+ >
17
+ > 从API version 20开始,@Consume装饰的变量支持设置默认值。当查找不到@Provide的匹配结果时,@Consume装饰的变量会使用默认值进行初始化;当查找到@Provide的匹配结果时,@Consume装饰的变量会优先使用@Provide匹配结果的值,默认值不生效。
18
+ >
19
+ > 从API version 20开始,通过配置[BuilderNode](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-arkui-buildernode)的[BuildOptions](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-arkui-buildernode#buildoptions12)参数enableProvideConsumeCrossing为true,使得@Provide和@Consume支持跨[BuilderNode](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-arkui-buildernode)双向同步。但需要注意,BuilderNode会在上树前构造节点,所以BuilderNode内部定义的@Consume需要设置默认值,并在BuilderNode上树后,重新获取最近的@Provide数据,与之建立双向同步关系。具体可见[@Consume在跨BuilderNode场景下和@Provide建立双向同步](#consume在跨buildernode场景下和provide建立双向同步)。
20
+
21
+ ## 概述
22
+
23
+ @Provide/@Consume装饰的状态变量有以下特性:
24
+
25
+ - @Provide装饰的状态变量自动对其所有后代组件可用,开发者不需要多次在组件之间传递变量。
26
+
27
+ - 后代通过使用@Consume获取@Provide提供的变量,建立在@Provide和@Consume之间的双向数据同步,与[@State](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-state)/[@Link](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-link)不同的是,前者可以更便捷的在多层级父子组件之间传递。
28
+
29
+ - @Provide和@Consume通过变量名或者变量别名绑定,需要类型相同,否则会发生类型隐式转换,从而导致应用行为异常。
30
+
31
+ ```ts
32
+ // 通过相同的变量名绑定
33
+ @Provide age: number = 0;
34
+ @Consume age: number;
35
+
36
+ // 通过相同的变量别名绑定
37
+ @Provide('a') id: number = 0;
38
+ @Consume('a') age: number;
39
+
40
+ // 通过Provide的变量别名和Consume的变量名相同绑定
41
+ @Provide('a') id: number = 0;
42
+ @Consume a: number;
43
+
44
+ // 通过Provide的变量名和Consume的变量别名绑定
45
+ @Provide id: number = 0;
46
+ @Consume('id') a: number;
47
+ ```
48
+
49
+ 当@Provide指定变量别名时,会同时保存变量名与变量别名,@Consume在查找时,会优先以变量别名作为查找值去匹配,如果没有别名则用变量名作为查找值,只要@Consume提供的查找值与@Provide保存的变量名或别名中任意一项一致,即可成功建立绑定关系。
50
+
51
+ ## 装饰器说明
52
+
53
+ | @Provide变量装饰器 | 说明 |
54
+ | --- | --- |
55
+ | 装饰器参数 | 别名:常量字符串,可选。如果指定了别名,则通过别名来绑定变量;如果未指定别名,则通过变量名绑定变量。allowOverride:允许重写,string类型,可选。如果使用allowOverride指定别名,则别名可以被重写,即可以存在同名的@Provide变量。未使用allowOverride时则不允许重名。示例见[@Provide支持allowOverride参数](#provide支持allowoverride参数)。 |
56
+ | 允许装饰的变量类型 | Object、class、string、number、boolean、enum类型,以及这些类型的数组。API version 10开始支持[Date类型](#装饰date类型变量)。API version 11及以上支持[Map](#装饰map类型变量)、[Set](#装饰set类型变量)类型、undefined和null类型、ArkUI框架定义的联合类型[Length](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-types#length)、[ResourceStr](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-types#resourcestr)、[ResourceColor](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-types#resourcecolor)类型以及这些类型的联合类型,示例见[@Provide和Consume支持联合类型实例](#provide和consume支持联合类型实例)。 |
57
+ | 不允许装饰的变量类型 | 不支持装饰Function类型。 |
58
+ | 初始化规则 | 必须定义本地默认值。可以从父组件传入非undefined类型变量,此时使用该传入变量进行初始化。父组件未传入或传入undefined类型变量时,使用本地默认值进行初始化。 |
59
+ | 同步规则 | **在子组件使用时:** 不与父组件中的任何类型变量同步。父组件传入的外部变量对@Provide初始化时,仅作为初始值,后续变量的变化不会同步至@Provide。**在父组件使用时:** 可以初始化子组件的常规变量、@State、@Link、[@Prop](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-prop)、@Provide。@Provide变量的变化会同步给子组件的@Link、@Prop变量。与后代子组件中别名匹配的@Consume变量双同步。 |
60
+
61
+ **图1** @Provide初始化规则图示
62
+
63
+ ![Provide初始化规则图示](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/eb/v3/nxSHM0cRS6C2VpPYkqXM7A/zh-cn_image_0000002535788142.png)
64
+
65
+ | @Consume变量装饰器 | 说明 |
66
+ | --- | --- |
67
+ | 装饰器参数 | 别名:常量字符串,可选。如果指定了别名,则通过别名来绑定变量;如果未指定别名,则通过变量名绑定变量。 |
68
+ | 允许装饰的变量类型 | Object、class、string、number、boolean、enum类型,以及这些类型的数组。API version 10开始支持[Date类型](#装饰date类型变量)。API version 11及以上支持[Map](#装饰map类型变量)、[Set](#装饰set类型变量)类型、undefined和null类型、ArkUI框架定义的联合类型[Length](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-types#length)、[ResourceStr](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-types#resourcestr)、[ResourceColor](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-types#resourcecolor)类型以及这些类型的联合类型,示例见[@Provide和Consume支持联合类型实例](#provide和consume支持联合类型实例)。**说明:** API version 20之前,@Consume装饰的变量,在其父组件或者祖先组件上,必须有对应的属性和别名的@Provide装饰的变量。 |
69
+ | 不允许装饰的变量类型 | 不支持装饰Function类型。 |
70
+ | 初始化规则 | API version 20之前,@Consume装饰的变量不支持本地设置默认值,必须要有与其匹配的@Provide装饰的变量。从API version 20开始,@Consume支持设置默认值。若存在匹配成功的@Provide,则会使用@Provide的变量值作为初始值。若未匹配到@Provide变量,则使用本地默认值。示例见[@Consume装饰的变量支持设置默认值](#consume装饰的变量支持设置默认值)。 |
71
+ | 同步规则 | **在子组件使用时:** 与祖先组件匹配的@Provide变量双向同步。**在父组件使用时:** 可以初始化子组件的常规变量、@State、@Link、@Prop、@Provide。@Consume变量的变化会同步给子组件的@Link、@Prop变量。 |
72
+
73
+ **图2** @Consume初始化规则图示
74
+
75
+ ![Consume初始化规则图示](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/6b/v3/doXElmGCQgGETXbBxX6DqA/zh-cn_image_0000002535948088.png)
76
+
77
+ ## 观察变化和行为表现
78
+
79
+ ### 观察变化
80
+
81
+ - 当装饰的数据类型为boolean、string、number类型时,可以观察到数值的变化。
82
+
83
+ - 当装饰的数据类型为class或者Object的时候,可以观察到赋值和属性赋值的变化(属性为Object.keys(observedObject)返回的所有属性)。
84
+
85
+ - 当装饰Array时,可以观察到数组本身、数组项的赋值及其API操作带来的变化。详见[装饰Array类型变量](#装饰array类型变量)。
86
+
87
+ - 当装饰的对象是Date时,可以观察到Date整体的赋值,同时可通过调用Date的接口setFullYear, setMonth, setDate, setHours, setMinutes, setSeconds, setMilliseconds, setTime, setUTCFullYear, setUTCMonth, setUTCDate, setUTCHours, setUTCMinutes, setUTCSeconds, setUTCMilliseconds 更新Date的属性,详见[装饰Date类型变量](#装饰date类型变量)。
88
+
89
+ - 当装饰的变量是Map时,可以观察到Map整体的赋值,同时可通过调用Map的接口set, clear, delete 更新Map的值。详见[装饰Map类型变量](#装饰map类型变量)。
90
+
91
+ - 当装饰的变量是Set时,可以观察到Set整体的赋值,同时可通过调用Set的接口add, clear, delete 更新Set的值。详见[装饰Set类型变量](#装饰set类型变量)。
92
+
93
+ ### 框架行为
94
+
95
+ 1. 初始渲染:
96
+
97
+ 1. @Provide装饰的变量会以Map的形式,传递给当前@Provide所属组件的所有子组件。
98
+ 2. 子组件中如果使用@Consume变量,则会在Map中查找是否有该变量名/alias(别名)对应的@Provide的变量。在API version 20之前,如果查找不到,框架会抛出JS ERROR。从API version 20开始,如果查找不到,会判断@Consume装饰的变量是否设置了默认值,如果没有设置默认值,框架会抛出JS ERROR。
99
+ 3. 在初始化@Consume变量时,如果在Map中有该变量名/alias(别名)对应的@Provide的变量,则和@State/@Link的流程类似,@Consume变量会在Map中查找到对应的@Provide变量进行保存,并把自己注册给@Provide。
100
+ 4. 从API version 20开始,在初始化@Consume变量时,如果在Map中没有该变量名/alias(别名)对应的@Provide的变量,而@Consume的变量设置了默认值时,@Consume变量会利用默认值创建一个临时的数据源,保证通知链路的连续性。
101
+
102
+ 2. 当@Provide装饰的数据变化时:
103
+
104
+ 1. 通过初始渲染的步骤可知,子组件@Consume已把自己注册给父组件。父组件@Provide变量变更后,会遍历更新所有依赖它的系统组件(elementid)和状态变量(@Consume)。
105
+ 2. 通知@Consume更新后,子组件所有依赖@Consume的系统组件(elementId)都会被通知更新。以此实现@Provide对@Consume状态数据同步。
106
+
107
+ 3. 当@Consume装饰的数据变化时:
108
+
109
+ 通过初始渲染的步骤可知,子组件@Consume持有@Provide的实例。在@Consume更新后调用@Provide的更新方法,将更新的数值同步回@Provide,以此实现@Consume向@Provide的同步更新。
110
+
111
+ ![框架行为图示](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/7a/v3/VCMqVECWRBOHsez6llxrRg/zh-cn_image_0000002566867921.png)
112
+
113
+ ## 限制条件
114
+
115
+ 1. @Provide/@Consume的参数key必须为string类型,否则编译时会报错。
116
+
117
+ ```ts
118
+ // 错误写法,编译报错
119
+ let change: number = 10;
120
+ @Provide(change) message: string = 'Hello';
121
+
122
+ // 正确写法
123
+ let change: string = 'change';
124
+ @Provide(change) message: string = 'Hello';
125
+ ```
126
+
127
+ 2. @Consume装饰的变量不能在构造参数中传入初始化,否则编译时会报错。@Consume仅能通过key来匹配对应的@Provide变量或者从API version 20开始设置默认值进行初始化。
128
+
129
+ 【反例】
130
+
131
+ ```ts
132
+ @Component
133
+ struct Child {
134
+ @Consume msg: string;
135
+
136
+ build() {
137
+ Text(this.msg)
138
+ }
139
+ }
140
+
141
+ @Entry
142
+ @Component
143
+ struct Parent {
144
+ @Provide message: string = 'Hello';
145
+
146
+ build() {
147
+ Column() {
148
+ // 错误写法,不允许外部传入初始化
149
+ Child({msg: 'Hello'})
150
+ }
151
+ }
152
+ }
153
+ ```
154
+
155
+ 【正例】
156
+
157
+ ```TypeScript
158
+ @Component
159
+ struct Child {
160
+ @Consume num: number;
161
+ // 从API version 20开始,@Consume装饰的变量支持设置默认值
162
+ @Consume num1: number = 17;
163
+
164
+ build() {
165
+ Column() {
166
+ Text(`Value of num: ${this.num}`)
167
+ Text(`Value of num1: ${this.num1}`)
168
+ }
169
+ }
170
+ }
171
+
172
+ @Entry
173
+ @Component
174
+ struct Parent {
175
+ @Provide num: number = 10;
176
+
177
+ build() {
178
+ Column() {
179
+ Text(`Value of num: ${this.num}`)
180
+ Child()
181
+ }
182
+ }
183
+ }
184
+ ```
185
+
186
+ 3. @Provide的key重复定义时,框架会抛出运行时错误,提醒开发者重复定义key,如果开发者需要重复key,可以使用[allowoverride](#provide支持allowoverride参数)。
187
+
188
+ ```ts
189
+ // 错误写法,a重复定义
190
+ @Provide('a') count: number = 10;
191
+ @Provide('a') num: number = 10;
192
+
193
+ // 正确写法
194
+ @Provide('a') count: number = 10;
195
+ @Provide('b') num: number = 10;
196
+ ```
197
+
198
+ 4. 在API version 20之前,初始化@Consume变量时,如果开发者没有定义对应key的@Provide变量,框架会抛出运行时错误,提示开发者初始化@Consume变量失败,原因是无法找到其对应key的@Provide变量。从API version 20开始,初始化@Consume变量时,如果开发者没有定义对应key的@Provide变量,同时没有设置默认值,框架会抛出运行时错误,提示开发者初始化@Consume变量失败,原因是无法找到其对应key的@Provide变量同时也没有设置默认值。
199
+
200
+ 【反例】
201
+
202
+ ```ts
203
+ @Component
204
+ struct Child {
205
+ @Consume num: number;
206
+
207
+ build() {
208
+ Column() {
209
+ Text(`num的值: ${this.num}`)
210
+ }
211
+ }
212
+ }
213
+
214
+ @Entry
215
+ @Component
216
+ struct Parent {
217
+ // 错误写法,缺少@Provide
218
+ num: number = 10;
219
+
220
+ build() {
221
+ Column() {
222
+ Text(`num的值: ${this.num}`)
223
+ Child()
224
+ }
225
+ }
226
+ }
227
+ ```
228
+
229
+ 【正例】
230
+
231
+ ```TypeScript
232
+ @Component
233
+ struct Child {
234
+ @Consume num: number;
235
+ // 正确写法 从API version 20开始,@Consume装饰的变量支持设置默认值
236
+ @Consume numWithDefaultValue: number = 6;
237
+
238
+ build() {
239
+ Column() {
240
+ Text(`Value of num: ${this.num}`)
241
+ Text(`Value of numWithDefaultValue: ${this.numWithDefaultValue}`)
242
+ }
243
+ }
244
+ }
245
+
246
+ @Entry
247
+ @Component
248
+ struct Parent {
249
+ // 正确写法
250
+ @Provide num: number = 10;
251
+
252
+ build() {
253
+ Column() {
254
+ Text(`Value of num: ${this.num}`)
255
+ Child()
256
+ }
257
+ }
258
+ }
259
+ ```
260
+
261
+ 5. @Provide与@Consume不支持装饰Function类型的变量,框架会抛出运行时错误。
262
+
263
+ 6. 从API version 20开始,支持跨BuilderNode配对@Provide/@Consume。在BuilderNode上树时,@Consume通过key匹配找到最近的@Provide,两者类型需要一致,如果不一致,则会抛出运行时错误。
264
+
265
+ 需要注意类型不相等判断,包括类实例的判断,比如:
266
+
267
+ ```ts
268
+ class A {}
269
+ class B {}
270
+ // 两个message都为object类型,但其构造函数不同,属于不同类型
271
+ @Provide message: A = new A();
272
+ @Consume message: B = new B();
273
+ ```
274
+
275
+ 在非BuilderNode场景中,仍建议配对的@Provide/@Consume类型一致。虽然在运行时不会有强校验,但在@Consume装饰的变量初始化时,会隐式转换成@Provide装饰变量的类型。
276
+
277
+ ```TypeScript
278
+ import { NodeController, BuilderNode, FrameNode, UIContext } from '@kit.ArkUI';
279
+
280
+ @Builder
281
+ function buildText() {
282
+ Column() {
283
+ Child()
284
+ }
285
+ }
286
+
287
+ class TextNodeController extends NodeController {
288
+ private builderNode: BuilderNode<[]> | null = null;
289
+
290
+ constructor() {
291
+ super();
292
+ }
293
+
294
+ makeNode(context: UIContext): FrameNode | null {
295
+ this.builderNode = new BuilderNode(context);
296
+ // 配置跨BuilderNode支持@Provide/@Consume
297
+ this.builderNode.build(wrapBuilder(buildText), undefined,
298
+ { enableProvideConsumeCrossing: true });
299
+ // 将BuilderNode的根节点挂载到NodeContainer
300
+ return this.builderNode.getFrameNode();
301
+ }
302
+ }
303
+
304
+ @Entry
305
+ @Component
306
+ struct Index {
307
+ @Provide message: string = 'hello';
308
+ controller: TextNodeController = new TextNodeController();
309
+
310
+ build() {
311
+ Column() {
312
+ NodeContainer(this.controller)
313
+ .width('100%')
314
+ .height(100)
315
+ }
316
+ .width('100%')
317
+ .height('100%')
318
+ }
319
+ }
320
+
321
+
322
+ @Component
323
+ struct Child {
324
+ // Child通过BuilderNode上树后,@Consume和Index中的@Provide建立连接时发现类型不一致,抛出运行时错误
325
+ @Consume message: number = 0;
326
+
327
+ build() {
328
+ Column() {
329
+ Text(`@Consume ${this.message}`)
330
+ }
331
+ }
332
+ }
333
+ ```
334
+
335
+ 7. 父组件传入undefined时,@Provide装饰的变量仍使用本地默认值进行初始化。
336
+
337
+ ```TypeScript
338
+ @Entry
339
+ @Component
340
+ struct Parent {
341
+ @State count: number | undefined = undefined;
342
+
343
+ build() {
344
+ Column() {
345
+ Text(`Parent count value: ${this.count}`)
346
+ .fontSize(20)
347
+ .margin(10)
348
+ Child({ count: this.count })
349
+ }
350
+ }
351
+ }
352
+
353
+ @Component
354
+ struct Child {
355
+ @Provide count: number | undefined = 0;
356
+
357
+ build() {
358
+ Column() {
359
+ Text(`Child count value: ${this.count}`)
360
+ .fontSize(20)
361
+ .margin(10)
362
+ }
363
+ }
364
+ }
365
+ ```
366
+
367
+ ## 使用场景
368
+
369
+ ### @Provide变量与@Consume变量建立双向绑定
370
+
371
+ 以下示例是@Provide变量与后代组件中@Consume变量进行双向同步的场景。当分别点击ToDo和ToDoItem组件内的Button时,count的更改会双向同步在ToDo和ToDoItem中。
372
+
373
+ ```TypeScript
374
+ @Component
375
+ struct ToDoItem {
376
+ // @Consume装饰的变量通过相同的属性名绑定其祖先组件ToDo内的@Provide装饰的变量
377
+ @Consume count: number;
378
+
379
+ build() {
380
+ Column() {
381
+ Text(`count(${this.count})`)
382
+ Button(`count(${this.count}), count + 1`)
383
+ .onClick(() => this.count += 1)
384
+ }
385
+ .width('50%')
386
+ }
387
+ }
388
+
389
+ @Component
390
+ struct ToDoList {
391
+ build() {
392
+ Row({ space: 5 }) {
393
+ ToDoItem()
394
+ ToDoItem()
395
+ }
396
+ }
397
+ }
398
+
399
+ @Component
400
+ struct ToDoDemo {
401
+ build() {
402
+ ToDoList()
403
+ }
404
+ }
405
+
406
+ @Entry
407
+ @Component
408
+ struct ToDo {
409
+ // @Provide装饰的变量count由入口组件ToDo提供其后代组件
410
+ @Provide count: number = 0;
411
+
412
+ build() {
413
+ Column() {
414
+ Button(`count(${this.count}), count + 1`)
415
+ .onClick(() => this.count += 1)
416
+ ToDoDemo()
417
+ }
418
+ }
419
+ }
420
+ ```
421
+
422
+ ### 装饰Array类型变量
423
+
424
+ 以下示例中,message类型为number[],点击Button改变message的值,视图会随之刷新。
425
+
426
+ ```TypeScript
427
+ @Entry
428
+ @Component
429
+ struct Index {
430
+ @Provide message: number[] = [0, 1, 2, 3];
431
+
432
+ build() {
433
+ Column() {
434
+ ForEach(this.message, (item: number) => {
435
+ Text(`Provide ${item}`)
436
+ .fontSize(20)
437
+ .margin(10)
438
+ })
439
+ // 新增数组元素,触发UI刷新
440
+ Button('Push element')
441
+ .onClick(() => {
442
+ this.message.push(4);
443
+ })
444
+ .width(300)
445
+ .margin(10)
446
+ // 删除数组元素,触发UI刷新
447
+ Button('Pop element')
448
+ .onClick(() => {
449
+ this.message.pop();
450
+ })
451
+ .width(300)
452
+ .margin(10)
453
+ Child()
454
+ }
455
+ }
456
+ }
457
+
458
+ @Component
459
+ struct Child {
460
+ @Consume message: number[] = [0, 1, 2, 3];
461
+
462
+ build() {
463
+ Row() {
464
+ Column() {
465
+ ForEach(this.message, (item: number) => {
466
+ Text(`Consume ${item}`)
467
+ .fontSize(20)
468
+ .margin(10)
469
+ })
470
+ // 对数组整体重新赋值,触发UI刷新
471
+ Button('Reset array')
472
+ .onClick(() => {
473
+ this.message = [9, 8, 7, 6];
474
+ })
475
+ .width(300)
476
+ .margin(10)
477
+ // 更新数组元素,触发UI刷新
478
+ Button('Modify element[0]')
479
+ .onClick(() => {
480
+ this.message[0] = 10;
481
+ })
482
+ .width(300)
483
+ .margin(10)
484
+ }
485
+ .width('100%')
486
+ }
487
+ }
488
+ }
489
+ ```
490
+
491
+ ### 装饰Map类型变量
492
+
493
+ > **说明:**
494
+ >
495
+ > 从API version 11开始,@Provide,@Consume支持Map类型。
496
+
497
+ 以下示例中,message类型为Map<number, string>,点击Button改变message的值,视图会随之刷新。
498
+
499
+ ```TypeScript
500
+ @Component
501
+ struct Child {
502
+ @Consume message: Map<number, string>
503
+
504
+ build() {
505
+ Column() {
506
+ ForEach(Array.from(this.message.entries()), (item: [number, string]) => {
507
+ Text(`${item[0]}`)
508
+ .fontSize(30)
509
+ Text(`${item[1]}`)
510
+ .fontSize(30)
511
+ Divider()
512
+ })
513
+ Button('Consume init Map')
514
+ .onClick(() => {
515
+ this.message = new Map([[0, 'a'], [1, 'b'], [3, 'c']]);
516
+ })
517
+ Button('Consume set new one')
518
+ .onClick(() => {
519
+ this.message.set(4, 'd');
520
+ })
521
+ Button('Consume clear')
522
+ .onClick(() => {
523
+ this.message.clear();
524
+ })
525
+ Button('Consume replace the first item')
526
+ .onClick(() => {
527
+ this.message.set(0, 'aa');
528
+ })
529
+ Button('Consume delete the first item')
530
+ .onClick(() => {
531
+ this.message.delete(0);
532
+ })
533
+ }
534
+ }
535
+ }
536
+
537
+
538
+ @Entry
539
+ @Component
540
+ struct MapSample {
541
+ @Provide message: Map<number, string> = new Map([[0, 'a'], [1, 'b'], [3, 'c']])
542
+
543
+ build() {
544
+ Row() {
545
+ Column() {
546
+ Button('Provide init Map')
547
+ .onClick(() => {
548
+ this.message = new Map([[0, 'a'], [1, 'b'], [3, 'c'], [4, 'd']]);
549
+ })
550
+ Child()
551
+ }
552
+ .width('100%')
553
+ }
554
+ .height('100%')
555
+ }
556
+ }
557
+ ```
558
+
559
+ ### 装饰Set类型变量
560
+
561
+ > **说明:**
562
+ >
563
+ > 从API version 11开始,@Provide,@Consume支持Set类型。
564
+
565
+ 以下示例中,message类型为Set<number>,点击Button改变message的值,视图会随之刷新。
566
+
567
+ ```TypeScript
568
+ @Component
569
+ struct Child {
570
+ @Consume message: Set<number>
571
+
572
+ build() {
573
+ Column() {
574
+ ForEach(Array.from(this.message.entries()), (item: [number, number]) => {
575
+ Text(`${item[0]}`)
576
+ .fontSize(30)
577
+ Divider()
578
+ })
579
+ Button('Consume init set')
580
+ .onClick(() => {
581
+ this.message = new Set([0, 1, 2, 3, 4]);
582
+ })
583
+ Button('Consume set new one')
584
+ .onClick(() => {
585
+ this.message.add(5);
586
+ })
587
+ Button('Consume clear')
588
+ .onClick(() => {
589
+ this.message.clear();
590
+ })
591
+ Button('Consume delete the first one')
592
+ .onClick(() => {
593
+ this.message.delete(0);
594
+ })
595
+ }
596
+ .width('100%')
597
+ }
598
+ }
599
+
600
+
601
+ @Entry
602
+ @Component
603
+ struct SetSample {
604
+ @Provide message: Set<number> = new Set([0, 1, 2, 3, 4])
605
+
606
+ build() {
607
+ Row() {
608
+ Column() {
609
+ Button('Provide init set')
610
+ .onClick(() => {
611
+ this.message = new Set([0, 1, 2, 3, 4, 5]);
612
+ })
613
+ Child()
614
+ }
615
+ .width('100%')
616
+ }
617
+ .height('100%')
618
+ }
619
+ }
620
+ ```
621
+
622
+ ### 装饰Date类型变量
623
+
624
+ 以下示例中,selectedDate类型为Date,点击Button改变selectedDate的值,视图会随之刷新。
625
+
626
+ ```TypeScript
627
+ @Component
628
+ struct Child {
629
+ @Consume selectedDate: Date;
630
+
631
+ build() {
632
+ Column() {
633
+ Button(`child increase the day by 1`)
634
+ .onClick(() => {
635
+ this.selectedDate.setDate(this.selectedDate.getDate() + 1);
636
+ })
637
+ Button('child update the new date')
638
+ .margin(10)
639
+ .onClick(() => {
640
+ this.selectedDate = new Date('2023-09-09');
641
+ })
642
+ DatePicker({
643
+ start: new Date('1970-1-1'),
644
+ end: new Date('2100-1-1'),
645
+ selected: this.selectedDate
646
+ })
647
+ }
648
+ }
649
+ }
650
+
651
+ @Entry
652
+ @Component
653
+ struct Parent {
654
+ @Provide selectedDate: Date = new Date('2021-08-08')
655
+
656
+ build() {
657
+ Column() {
658
+ Button('parent increase the day by 1')
659
+ .margin(10)
660
+ .onClick(() => {
661
+ this.selectedDate.setDate(this.selectedDate.getDate() + 1);
662
+ })
663
+ Button('parent update the new date')
664
+ .margin(10)
665
+ .onClick(() => {
666
+ this.selectedDate = new Date('2023-07-07');
667
+ })
668
+ DatePicker({
669
+ start: new Date('1970-1-1'),
670
+ end: new Date('2100-1-1'),
671
+ selected: this.selectedDate
672
+ })
673
+ Child()
674
+ }
675
+ }
676
+ }
677
+ ```
678
+
679
+ ### Provide和Consume支持联合类型实例
680
+
681
+ @Provide和@Consume支持联合类型和undefined和null。以下示例中,count类型为string | undefined,当点击父组件Parent中的Button改变count的属性或者类型时,Child中也会对应刷新。
682
+
683
+ ```TypeScript
684
+ @Component
685
+ struct Child {
686
+ // @Consume装饰的变量通过相同的属性名绑定其祖先组件Ancestors内的@Provide装饰的变量
687
+ @Consume count: string | undefined;
688
+
689
+ build() {
690
+ Column() {
691
+ Text(`count(${this.count})`)
692
+ Button(`count(${this.count}), Child`)
693
+ .onClick(() => this.count = 'Ancestors')
694
+ }
695
+ .width('50%')
696
+ }
697
+ }
698
+
699
+ @Component
700
+ struct Parent {
701
+ build() {
702
+ Row({ space: 5 }) {
703
+ Child()
704
+ }
705
+ }
706
+ }
707
+
708
+ @Entry
709
+ @Component
710
+ struct Ancestors {
711
+ // @Provide装饰的联合类型count由入口组件Ancestors提供其后代组件
712
+ @Provide count: string | undefined = 'Child';
713
+
714
+ build() {
715
+ Column() {
716
+ Button(`count(${this.count}), Child`)
717
+ .onClick(() => this.count = undefined)
718
+ Parent()
719
+ }
720
+ }
721
+ }
722
+ ```
723
+
724
+ ### @Provide支持allowOverride参数
725
+
726
+ allowOverride:@Provide重写选项。
727
+
728
+ > **说明:**
729
+ >
730
+ > 从API version 11开始使用。
731
+
732
+ | 名称 | 类型 | 必填 | 说明 |
733
+ | --- | --- | --- | --- |
734
+ | allowOverride | string | 否 | 是否允许@Provide重写。允许在同一组件树下通过allowOverride重写同名的@Provide。如果开发者未写allowOverride,定义同名的@Provide,运行时会报错。 |
735
+
736
+ ```TypeScript
737
+ @Component
738
+ struct MyComponent {
739
+ @Provide({ allowOverride: 'reviewVotes' }) reviewVotes: number = 10;
740
+
741
+ build() {
742
+ }
743
+
744
+ }
745
+ ```
746
+
747
+ 完整示例如下:
748
+
749
+ ```TypeScript
750
+ @Component
751
+ struct GrandSon {
752
+ // @Consume装饰的变量通过相同的属性名绑定其祖先内的@Provide装饰的变量
753
+ @Consume('reviewVotes') reviewVotes: number;
754
+
755
+ build() {
756
+ Column() {
757
+ Text(`reviewVotes(${this.reviewVotes})`) // Text显示10
758
+ Button(`reviewVotes(${this.reviewVotes}), give +1`)
759
+ .onClick(() => this.reviewVotes += 1)
760
+ }
761
+ .width('50%')
762
+ }
763
+ }
764
+
765
+ @Component
766
+ struct Child {
767
+ @Provide({ allowOverride: 'reviewVotes' }) reviewVotes: number = 10;
768
+
769
+ build() {
770
+ Row({ space: 5 }) {
771
+ GrandSon()
772
+ }
773
+ }
774
+ }
775
+
776
+ @Component
777
+ struct Parent {
778
+ @Provide({ allowOverride: 'reviewVotes' }) reviewVotes: number = 20;
779
+
780
+ build() {
781
+ Child()
782
+ }
783
+ }
784
+
785
+ @Entry
786
+ @Component
787
+ struct GrandParent {
788
+ @Provide('reviewVotes') reviewVotes: number = 40;
789
+
790
+ build() {
791
+ Column() {
792
+ Button(`reviewVotes(${this.reviewVotes}), give +1`)
793
+ .onClick(() => this.reviewVotes += 1)
794
+ Parent()
795
+ }
796
+ }
797
+ }
798
+ ```
799
+
800
+ 在上面的示例中:
801
+
802
+ - GrandParent声明了@Provide('reviewVotes') reviewVotes: number = 40。
803
+ - Parent是GrandParent的子组件,声明@Provide为allowOverride,重写父组件GrandParent的@Provide('reviewVotes') reviewVotes: number = 40。如果不设置allowOverride,则会抛出运行时报错,提示@Provide重复定义。Child同理。
804
+ - GrandSon在初始化@Consume的时候,@Consume装饰的变量通过相同的属性名绑定其最近的祖先的@Provide装饰的变量。
805
+ - GrandSon查找到相同属性名的@Provide在祖先Child中,所以@Consume('reviewVotes') reviewVotes: number初始化数值为10。如果Child中没有定义与@Consume同名的@Provide,则继续向上寻找Parent中的同名@Provide值为20,以此类推。
806
+ - 如果查找到根节点还没有找到key对应的@Provide,则会报初始化@Consume找不到@Provide的报错。
807
+
808
+ ### @Consume装饰的变量支持设置默认值
809
+
810
+ > **说明:**
811
+ >
812
+ > 从API version 20开始,@Consume装饰的变量支持设置默认值。
813
+
814
+ ```TypeScript
815
+ @Component
816
+ struct MyComponent {
817
+ @Consume('withDefault') defaultValue: number = 10;
818
+
819
+ build() {
820
+ }
821
+
822
+ }
823
+ ```
824
+
825
+ 完整示例如下:
826
+
827
+ ```TypeScript
828
+ @Entry
829
+ @Component
830
+ struct Parent {
831
+ @Provide('firstKey') provideOne: string | undefined = undefined;
832
+ @Provide('secondKey') provideTwo: string = 'the second provider';
833
+
834
+ build() {
835
+ Column() {
836
+ Row() {
837
+ Column() {
838
+ Text(`${this.provideOne}`)
839
+ Text(`${this.provideTwo}`)
840
+ }
841
+
842
+ Column() {
843
+ // 点击change provideOne按钮,provideOne和子组件中的textOne属性会同时变化
844
+ Button('change provideOne')
845
+ .onClick(() => {
846
+ this.provideOne = undefined;
847
+ })
848
+ // 点击change provideTwo按钮,provideTwo和子组件中的textTwo属性会同时变化
849
+ Button('change provideTwo')
850
+ .onClick(() => {
851
+ this.provideTwo = 'the next provider';
852
+ })
853
+ }
854
+ }
855
+
856
+ Row() {
857
+ Column() {
858
+ Child()
859
+ }
860
+ }
861
+ }
862
+ }
863
+ }
864
+
865
+ @Component
866
+ struct Child {
867
+ // @Consume装饰的变量通过相同的别名绑定其祖先内的@Provide装饰的变量,同时设置默认值
868
+ @Consume('firstKey') textOne: string | undefined = 'child';
869
+ // @Consume装饰的变量通过相同的别名绑定其祖先内的@Provide装饰的变量,没有设置默认值
870
+ @Consume('secondKey') textTwo: string;
871
+ // @Consume装饰的变量在祖先内没有匹配成功的@Provide装饰的变量,但设置了默认值
872
+ @Consume('thirdKey') textThree: string = 'defaultValue';
873
+
874
+ build() {
875
+ Column() {
876
+ Text(`${this.textOne}`)
877
+ Text(`${this.textTwo}`)
878
+ Text(`${this.textThree}`)
879
+ // 点击change textOne按钮,textOne和父组件的provideOne会同时变化
880
+ Button('change textOne')
881
+ .onClick(() => {
882
+ this.textOne = 'not undefined';
883
+ })
884
+ // 点击change textTwo按钮,textTwo和父组件的provideTwo会同时变化
885
+ Button('change textTwo')
886
+ .onClick(() => {
887
+ this.textTwo = 'change textTwo';
888
+ })
889
+ }
890
+ }
891
+ }
892
+ ```
893
+
894
+ 在上面的示例中:
895
+
896
+ - Parent声明了@Provide('firstKey') provideOne: string | undefined = undefined 与 @Provide('secondKey') provideTwo: string = 'the second provider'。
897
+ - Child声明了@Consume('firstKey') textOne: string | undefined = 'child',@Consume('secondKey') textTwo: string 与 @Consume('thirdKey') textThree: string = 'defaultValue'。
898
+ - Child是Parent的子组件,Child在初始化@Consume装饰的三个属性时,textOne根据'firstKey'别名绑定Parent中的provideOne属性,provideOne的值会覆盖textOne的默认值,所以textOne初始化的值为undefined;textTwo根据'secondKey'别名绑定Parent中的providedTwo属性,textTwo初始化的值为'the second provider';textThree在祖先组件中不存在匹配结果,如果@Consume没有设置默认值,则会抛出运行时错误,示例中textThree有默认值'defaultValue',所以textThree初始化的值为'defaultValue'。
899
+ - @Consume装饰的属性设置的默认值仅在祖先组件没有匹配结果时才生效,有匹配结果时无影响。
900
+
901
+ ### @Consume在跨BuilderNode场景下和@Provide建立双向同步
902
+
903
+ > **说明:**
904
+ >
905
+ > 从API version 20开始,支持跨BuilderNode配对@Provide/@Consume。
906
+
907
+ BuilderNode支持@Provide/@Consume,需注意:
908
+
909
+ 1. 在BuilderNode子树中定义的@Consume需要设置默认值,或者在子树中已存在配对的@Provide,否则会发生运行时报错。
910
+ 2. BuilderNode上树后,设置默认值的@Consume会向上查找@Provide,根据key的匹配规则找到最近的@Provide后,会和@Provide建立双向同步关系。如果找不到配对的@Provide,则@Consume仍使用默认值。
911
+ 3. 建立双向同步的关系后,如果@Provide装饰变量的值和@Consume的默认值不同,则会回调@Consume的[@Watch](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-watch)方法,以及与@Consume有同步关系的变量的@Watch方法,例如@Consume通知与其双向同步的@Link触发@Watch方法。
912
+ 4. BuilderNode下树后,@Consume会再次试图查找对应的@Provide,如果发现下树后无法再找到之前配对的@Provide,则断开和@Provide的双向同步关系,@Consume装饰的变量恢复成默认值。
913
+ 5. @Consume断开和@Provide的连接,恢复成默认值时,会判断@Consume装饰变量的值从和@Provide变为@Consume的默认值是否有变化,如果有变化,则会回调@Consume以及与其有同步关系变量的@Watch方法。
914
+
915
+ 在下面的例子中:
916
+
917
+ 1. 点击add Child:
918
+ - 构建BuilderNode下的子节点Child,Child中@Consume未找到@Provide,使用本地默认值default value初始化。
919
+ - BuilderNode上树时,Child中@Consume向上找到最近的Index中的@Provide,将@Consume从默认值更新为@Provide的值,并回调@Consume的@Watch方法。
920
+
921
+ 2. @Provide和@Consume配对后,建立双向同步关系。点击Text(`@Provide: ${this.message}`)和Text(`@Consume ${this.message}`),@Provide和@Consume绑定的Text组件刷新,并回调@Provide和@Consume的@Watch方法。
922
+
923
+ 3. 点击remove Child, BuilderNode子节点下树,Child中的@Consume和Index中的@Provide断开连接,Child中的@Consume恢复成默认值,并回调@Consume的@Watch方法。
924
+
925
+ 4. 点击dispose Child,释放BuilderNode下子节点,BuilderNode子节点Child销毁,执行[aboutToDisappear](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-custom-component-lifecycle#abouttodisappear)。
926
+
927
+ ```TypeScript
928
+ import { NodeController, BuilderNode, FrameNode, UIContext } from '@kit.ArkUI';
929
+ import { hilog } from '@kit.PerformanceAnalysisKit';
930
+
931
+ const DOMAIN = 0x0000;
932
+
933
+ @Builder
934
+ function buildText() {
935
+ Column() {
936
+ Child()
937
+ }
938
+ }
939
+
940
+ class TextNodeController extends NodeController {
941
+ private rootNode: FrameNode | null = null;
942
+ private uiContext: UIContext | null = null;
943
+ private builderNode: BuilderNode<[]> | null = null;
944
+
945
+ constructor() {
946
+ super();
947
+ }
948
+
949
+ makeNode(context: UIContext): FrameNode | null {
950
+ this.rootNode = new FrameNode(context);
951
+ this.uiContext = context;
952
+ // 将rootNode节点挂载在NodeContainer下
953
+ return this.rootNode;
954
+ }
955
+
956
+ addBuilderNode(): void {
957
+ if (this.builderNode === null && this.uiContext && this.rootNode) {
958
+ this.builderNode = new BuilderNode(this.uiContext);
959
+ // 配置跨BuilderNode支持@Provide/@Consume
960
+ this.builderNode.build(wrapBuilder(buildText), undefined,
961
+ { enableProvideConsumeCrossing: true });
962
+ // 将BuilderNode的根节点挂载到rootNode节点下
963
+ this.rootNode.appendChild(this.builderNode.getFrameNode());
964
+ }
965
+ }
966
+
967
+ removeBuilderNode(): void {
968
+ if (this.rootNode && this.builderNode) {
969
+ // 从rootNode节点下的BuildNode节点移除
970
+ this.rootNode.removeChild(this.builderNode.getFrameNode());
971
+ }
972
+ }
973
+
974
+ disposeNode(): void {
975
+ if (this.rootNode && this.builderNode) {
976
+ // 立即释放当前BuilderNode
977
+ this.builderNode.dispose();
978
+ }
979
+ }
980
+ }
981
+
982
+ @Entry
983
+ @Component
984
+ struct Index {
985
+ @Provide @Watch('onChange') message: string = 'hello';
986
+ controller: TextNodeController = new TextNodeController();
987
+
988
+ onChange() {
989
+ hilog.info(DOMAIN, 'testTag', '%{public}s', `Index Provide change ${this.message}`);
990
+ }
991
+
992
+ build() {
993
+ Column() {
994
+ Text(`@Provide: ${this.message}`)
995
+ .fontSize(20)
996
+ .onClick(() => {
997
+ this.message += ' Provide';
998
+ })
999
+
1000
+ // 执行BuilderNode的build方法,构造Child自定义组件
1001
+ // 并将BuilderNode挂载在NodeContainer下
1002
+ // Child中@Consume可以和当前Index中的@Provide配对
1003
+ // @Consume装饰的变量message从default value变为hello,并回调@Consume的@Watch方法
1004
+ Button('add Child')
1005
+ .onClick(() => {
1006
+ this.controller.addBuilderNode();
1007
+ })
1008
+ // 将BuilderNode下的节点从NodeContainer上移除
1009
+ // @Consume修饰的变量message从和@Provide配对的值变为default value,并回调@Consume的@Watch方法
1010
+ Button('remove Child')
1011
+ .onClick(() => {
1012
+ this.controller.removeBuilderNode();
1013
+ })
1014
+
1015
+ // 立即释放当前BuilderNode,BuilderNode下节点销毁,Child组件执行aboutToDisappear
1016
+ Button('dispose Child')
1017
+ .onClick(() => {
1018
+ this.controller.disposeNode();
1019
+ })
1020
+ NodeContainer(this.controller)
1021
+ .width('100%')
1022
+ .height(100)
1023
+ .backgroundColor(Color.Pink)
1024
+ }
1025
+ .width('100%')
1026
+ .height('100%')
1027
+ }
1028
+ }
1029
+
1030
+
1031
+ @Component
1032
+ struct Child {
1033
+ @Consume @Watch('onChange') message: string = 'default value';
1034
+
1035
+ onChange() {
1036
+ hilog.info(DOMAIN, 'testTag', '%{public}s', `Child Consume change ${this.message}`);
1037
+ }
1038
+
1039
+ aboutToDisappear(): void {
1040
+ hilog.info(DOMAIN, 'testTag', '%{public}s', `Child aboutToDisappear`);
1041
+ }
1042
+
1043
+ build() {
1044
+ Column() {
1045
+ Text(`@Consume ${this.message}`)
1046
+ .fontSize(20)
1047
+ .onClick(() => {
1048
+ this.message += ' Consume';
1049
+ })
1050
+ }
1051
+ }
1052
+ }
1053
+ ```
1054
+
1055
+ ## 常见问题
1056
+
1057
+ ### @BuilderParam尾随闭包情况下@Provide未定义错误
1058
+
1059
+ 在此[尾随闭包](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-builderparam#尾随闭包初始化组件)场景下,CustomWidget执行this.builder()创建子组件CustomWidgetChild时,this指向的是HomePage。因此找不到CustomWidget的@Provide变量,所以下面示例会报找不到@Provide错误,和@BuilderParam连用的时候要谨慎this的指向。
1060
+
1061
+ 错误示例:
1062
+
1063
+ ```ts
1064
+ class Tmp {
1065
+ a: string = '';
1066
+ }
1067
+
1068
+ @Entry
1069
+ @Component
1070
+ struct HomePage {
1071
+ // 错误点1:HomePage未声明@Provide
1072
+ @Builder
1073
+ builder2($$: Tmp) {
1074
+ Text(`${$$.a}测试`)
1075
+ }
1076
+
1077
+ build() {
1078
+ Column() {
1079
+ // 错误点2:使用尾随闭包的形式将创建CustomWidgetChild的函数传递给CustomWidget,此时尾随闭包中this指向HomePage
1080
+ CustomWidget() {
1081
+ CustomWidgetChild({ builder: this.builder2 })
1082
+ }
1083
+ }
1084
+ }
1085
+ }
1086
+
1087
+ @Component
1088
+ struct CustomWidget {
1089
+ // 错误点3:@Provide变量声明在CustomWidget中,仅有CustomWidget自身及其子组件能够消费
1090
+ @Provide('a') a: string = 'abc';
1091
+ @BuilderParam
1092
+ builder: () => void;
1093
+
1094
+ build() {
1095
+ Column() {
1096
+ Button('你好').onClick(() => {
1097
+ if (this.a == 'ddd') {
1098
+ this.a = 'abc';
1099
+ }
1100
+ else {
1101
+ this.a = 'ddd';
1102
+ }
1103
+
1104
+ })
1105
+ this.builder()
1106
+ }
1107
+ }
1108
+ }
1109
+
1110
+ @Component
1111
+ struct CustomWidgetChild {
1112
+ // 错误点4:尝试消费CustomWidget的@Provide('a'),但实际上CustomWidgetChild的父组件为HomePage,无法找到对应的@Provide
1113
+ @Consume('a') a: string;
1114
+ @BuilderParam
1115
+ builder: ($$: Tmp) => void;
1116
+
1117
+ build() {
1118
+ Column() {
1119
+ this.builder({ a: this.a })
1120
+ }
1121
+ }
1122
+ }
1123
+ ```
1124
+
1125
+ 正确示例:
1126
+
1127
+ ```TypeScript
1128
+ class Tmp {
1129
+ public name: string = '';
1130
+ }
1131
+
1132
+ @Entry
1133
+ @Component
1134
+ struct HomePage {
1135
+ // 修正点1:将@Provide声明在Entry组件(根作用域),确保子组件能正确消费
1136
+ @Provide('name') name: string = 'abc';
1137
+
1138
+ @Builder
1139
+ builder2($$: Tmp) {
1140
+ Text(`${$$.name} test`)
1141
+ }
1142
+
1143
+ build() {
1144
+ Column() {
1145
+ Button('Hello').onClick(() => {
1146
+ if (this.name == 'ddd') {
1147
+ this.name = 'abc';
1148
+ } else {
1149
+ this.name = 'ddd';
1150
+ }
1151
+ })
1152
+ // 修正点2:CustomWidget不再声明@Provide,仅作为容器传递builder
1153
+ CustomWidget() {
1154
+ CustomWidgetChild({ builder: this.builder2 })
1155
+ }
1156
+ }
1157
+ }
1158
+ }
1159
+
1160
+ @Component
1161
+ struct CustomWidget {
1162
+ @BuilderParam
1163
+ builder: () => void;
1164
+
1165
+ build() {
1166
+ this.builder()
1167
+ }
1168
+ }
1169
+
1170
+ @Component
1171
+ struct CustomWidgetChild {
1172
+ // 修正点3:@Consume从根作用域(HomePage)获取@Provide('name'),作用域正确
1173
+ @Consume('name') name: string;
1174
+ @BuilderParam
1175
+ builder: ($$: Tmp) => void;
1176
+
1177
+ build() {
1178
+ Column() {
1179
+ this.builder({ name: this.name })
1180
+ }
1181
+ }
1182
+ }
1183
+ ```