当前位置:   article > 正文

梅克尔工作室-赵一帆-鸿蒙笔记3_鸿蒙ondetached()

鸿蒙ondetached()

1.list组件与listitem组件

<1>list组件

列表包含一系列相同宽度的列表项。适合连续、多行呈现同类数据,例如图片和文本。

子组件

仅支持<list-item-group>和<list-item>。

属性

除支持通用属性外,还支持如下属性:

样式

除支持通用样式外,还支持如下样式:

事件

除支持通用事件外,还支持如下事件:

方法

支持通用方法外,还支持如下方法:

表1 currentOffset返回对象属性说明

示例

  1. <!-- index.hml -->
  2. <div class="container">
  3. <list class="todo-wrapper">
  4. <list-item for="{{todolist}}" class="todo-item">
  5. <div style="flex-direction: column;align-items: center;justify-content: center;">
  6. <text class="todo-title">{{$item.title}}</text>
  7. <text class="todo-title">{{$item.date}}</text>
  8. </div>
  9. </list-item>
  10. </list>
  11. </div>
  1. /* index.css */
  2. .container {
  3. display: flex;
  4. justify-content: center;
  5. align-items: center;
  6. width: 100%;
  7. height: 100%;
  8. }
  9. .todo-wrapper {
  10. width: 100%;
  11. height: 300px;
  12. }
  13. .todo-item {
  14. width: 100%;
  15. height: 120px;
  16. justify-content:center;
  17. }
  18. .todo-title {
  19. width: 100%;
  20. height: 80px;
  21. text-align: center;
  22. }
  1. // index.js
  2. export default {
  3. data: {
  4. todolist: [{
  5. title: '刷题',
  6. date: '2021-12-31 10:00:00'
  7. }, {
  8. title: '看电影',
  9. date: '2021-12-31 20:00:00'
  10. }],
  11. },
  12. }
<2>listitem组件

<list>的子组件,用来展示列表具体item。由于父元素list组件的align-items默认样式为stretch,该组件宽度默认充满list组件。设置父元素list组件的align-items样式为非stretch来生效自定义宽度。

子组件

支持单个子组件。

属性

除支持通用属性外,还支持如下属性:

样式

除支持通用样式外,还支持如下样式:

事件

除支持通用事件外,还支持如下事件:

方法

支持通用方法

示例

详见List示例

2.自定义组件

自定义组件的基本用法

自定义组件是用户根据业务需求,将已有的组件组合,封装成的新组件,可以在工程中多次调用,从而提高代码的可读性。自定义组件通过element引入到宿主页面,使用方法如下:

  1. <element name='comp' src='../../common/component/comp.hml'></element>
  2. <div>
  3. <comp prop1='xxxx' @child1="bindParentVmMethod"></comp>
  4. </div>

结合if-else使用自定义组件的示例,showComp1为true时显示自定义组件comp1,否则显示comp2:

  1. <element name='comp1' src='../../common/component/comp1/comp1.hml'></element>
  2. <element name='comp2' src='../../common/component/comp2/comp2.hml'></element>
  3. <div>
  4. <comp1 if="{{showComp1}}" prop1='xxxx' @child1="bindParentVmMethodOne"></comp1>
  5. <comp2 else prop1='xxxx' @child1="bindParentVmMethodTwo"></comp2>
  6. </div>

自定义组件的name属性指自定义组件名称(非必填),组件名称对大小写不敏感,默认使用小写。src属性指自定义组件hml文件路径(必填),若没有设置name属性,则默认使用hml文件名作为组件名。

自定义事件

父组件中绑定自定义子组件的事件使用(on|@)event-name="bindParentVmMethod"语法,子组件中通过this.$emit(‘eventName’, { params: ‘传递参数’ })触发事件并向上传递参数,父组件执行bindParentVmMethod方法并接收子组件传递的参数。

说明:
子组件中使用驼峰命名法命名的事件,在父组件中绑定时需要使用短横线分隔命名形式,例如:@children-event表示绑定子组件的childrenEvent事件。

示例1:无参数传递

子组件comp定义如下:

  1. <!-- comp.hml -->
  2. <div class="item">
  3. <text class="text-style" onclick="childClicked">点击这里查看隐藏文本</text>
  4. <text class="text-style" if="{{showObj}}">hello world</text>
  5. </div>
  1. /* comp.css */
  2. .item {
  3. width: 700px;
  4. flex-direction: column;
  5. height: 300px;
  6. align-items: center;
  7. margin-top: 100px;
  8. }
  9. .text-style {
  10. font-weight: 500;
  11. font-family: Courier;
  12. font-size: 40px;
  13. }
  1. // comp.js
  2. export default {
  3. data: {
  4. showObj: false,
  5. },
  6. childClicked () {
  7. this.$emit('eventType1');
  8. this.showObj = !this.showObj;
  9. },
  10. }

引入子组件comp的父组件示例如下:

  1. <!-- xxx.hml -->
  2. <element name='comp' src='../../common/component/comp.hml'></element>
  3. <div class="container">
  4. <comp @event-type1="textClicked"></comp>
  5. </div>
  1. /* xxx.css */
  2. .container {
  3. background-color: #f8f8ff;
  4. flex: 1;
  5. flex-direction: column;
  6. align-content: center;
  7. }
  1. // xxx.js
  2. export default {
  3. textClicked () {}
  4. }

示例2:有参数传递

子组件comp定义如下:

  1. <!-- comp.hml -->
  2. <div class="item">
  3. <text class="text-style" onclick="childClicked">点击这里查看隐藏文本</text>
  4. <text class="text-style" if="{{ showObj }}">hello world</text>
  5. </div>
  1. // comp.js
  2. export default {
  3. childClicked () {
  4. this.$emit('eventType1', { text: '收到子组件参数' });
  5. this.showObj = !this.showObj;
  6. },
  7. }

子组件向上传递参数text,父组件接收时通过e.detail来获取该参数:

  1. <!-- xxx.hml -->
  2. <element name='comp' src='../../common/comp/comp.hml'></element>
  3. <div class="container">
  4. <text>父组件:{{text}}</text>
  5. <comp @event-type1="textClicked"></comp>
  6. </div>
  1. // xxx.js
  2. export default {
  3. data: {
  4. text: '开始',
  5. },
  6. textClicked (e) {
  7. this.text = e.detail.text;
  8. },
  9. }

自定义组件数据

自定义组件的js文件中可以通过声明data、props、computed等字段完成数据的定义、传递与处理,其中props与computed的具体使用请参考数据传递与处理章节。

表1 自定义组件数据

Props

自定义组件可以通过props声明属性,父组件通过设置属性向子组件传递参数,props支持类型包括:String,Number,Boolean,Array,Object,Function。camelCase (驼峰命名法) 的 prop 名,在外部父组件传递参数时需要使用 kebab-case (短横线分隔命名) 形式,即当属性compProp在父组件引用时需要转换为comp-prop。给自定义组件添加props,通过父组件向下传递参数的示例如下:

  1. <!-- comp.hml -->
  2. <div class="item">
  3. <text class="title-style">{{compProp}}</text>
  4. </div>
  1. // comp.js
  2. export default {
  3. props: ['compProp'],
  4. }
  1. <!-- xxx.hml -->
  2. <element name='comp' src='../../common/component/comp/comp.hml'></element>
  3. <div class="container">
  4. <comp comp-prop="{{title}}"></comp>
  5. </div>
说明:
自定义属性命名时禁止以on、@、on:、grab: 等保留关键字为开头。

添加默认值

子组件可以通过固定值default设置默认值,当父组件没有设置该属性时,将使用其默认值。此情况下props属性必须为对象形式,不能用数组形式,示例如下:

  1. <!-- comp.hml -->
  2. <div class="item">
  3. <text class="title-style">{{title}}</text>
  4. </div>
  1. // comp.js
  2. export default {
  3. props: {
  4. title: {
  5. default: 'title',
  6. },
  7. },
  8. }

本示例中加入了一个text组件显示标题,标题的内容是一个自定义属性,显示用户设置的标题内容,当用户没有设置时显示默认值title。在引用该组件时添加该属性的设置:

  1. <!-- xxx.hml -->
  2. <element name='comp' src='../../common/component/comp/comp.hml'></element>
  3. <div class="container">
  4. <comp title="自定义组件"></comp>
  5. </div>

数据单向性

父子组件之间数据的传递是单向的,只能从父组件传递给子组件,子组件不能直接修改父组件传递下来的值,可以将props传入的值用data接收后作为默认值,再对data的值进行修改。

  1. // comp.js
  2. export default {
  3. props: ['defaultCount'],
  4. data() {
  5. return {
  6. count: this.defaultCount,
  7. };
  8. },
  9. onClick() {
  10. this.count = this.count + 1;
  11. },
  12. }

$watch 感知数据改变

如果需要观察组件中属性变化,可以通过$watch方法增加属性变化回调。使用方法如下:

  1. // comp.js
  2. export default {
  3. props: ['title'],
  4. onInit() {
  5. this.$watch('title', 'onPropertyChange');
  6. },
  7. onPropertyChange(newV, oldV) {
  8. console.info('title 属性变化 ' + newV + ' ' + oldV);
  9. },
  10. }

computed

自定义组件中经常需要在读取或设置某个属性时进行预先处理,提高开发效率,此种情况就需要使用computed计算属性。computed字段中可通过设置属性的getter和setter方法在属性读写的时候进行触发,使用方式如下:

  1. // comp.js
  2. export default {
  3. props: ['title'],
  4. data() {
  5. return {
  6. objTitle: this.title,
  7. time: 'Today',
  8. };
  9. },
  10. computed: {
  11. message() {
  12. return this.time + ' ' + this.objTitle;
  13. },
  14. notice: {
  15. get() {
  16. return this.time;
  17. },
  18. set(newValue) {
  19. this.time = newValue;
  20. },
  21. },
  22. },
  23. onClick() {
  24. console.info('get click event ' + this.message);
  25. this.notice = 'Tomorrow';
  26. },
  27. }

这里声明的第一个计算属性message默认只有getter函数,message的值会取决于objTitle的值的变化。getter函数只能读取不能改变参数值,例如data中初始化定义的time,当需要赋值给计算属性的时候可以提供一个setter函数,如示例中的notice。

继承样式

说明:
从API version 9开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。

自定义组件具有inherit-class属性,定义如下:

可以通过设置inherit-calss属性来继承父组件的样式。

父组件的hml文件,其中自定义组件comp通过inherit-class属性来指定继承其父组件的样式,即parent-class1和parent-class2:

  1. <!-- xxx.hml -->
  2. <element name='comp' src='../../common/component/comp.hml'></element>
  3. <div class="container">
  4. <comp inherit-class="parent-class1 parent-class2" ></comp>
  5. </div>

父组件的css文件:

  1. /* xxx.css */
  2. .parent-class1 {
  3. background-color:red;
  4. border:2px;
  5. }
  6. .parent-class2 {
  7. background-color:green;
  8. border:2px;
  9. }

自定义组件的hml文件,其中parent-class1和parent-class2是从父组件继承的样式:

  1. <!--comp.hml-->
  2. <div class="item">
  3. <text class="parent-class1">继承父组件的样式1</text>
  4. <text class="parent-class2">继承父组件的样式2</text>
  5. </div>

slot插槽

说明:
从API Version 7 开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。

默认插槽

自定义组件中通过slot标签来承载父组件中定义的内容,使用slot标签可以更加灵活的控制自定义组件的内容元素,使用方式如下:

  1. <!-- comp.hml -->
  2. <div class="item">
  3. <text class="text-style">下面使用父组件定义的内容</text>
  4. <slot></slot>
  5. </div>

引用该自定义组件方式如下:

  1. <!-- xxx.hml -->
  2. <element name='comp' src='../../common/component/comp.hml'></element>
  3. <div class="container">
  4. <comp>
  5. <text class="text-style">父组件中定义的内容</text>
  6. </comp>
  7. </div>

具名插槽

当自定义组件中需要使用多个插槽时,可通过对插槽命名的方式进行区分,当填充插槽内容时,通过声明插槽名称,将内容加到对应的插槽中。

  1. <!-- comp.hml -->
  2. <div class="item">
  3. <text class="text-style">下面使用父组件定义的内容</text>
  4. <slot name="first"></slot>
  5. <slot name="second"></slot>
  6. </div>

引用该自定义组件方式如下:

  1. <!-- xxx.hml -->
  2. <element name='comp' src='../../common/component/comp.hml'></element>
  3. <div class="container">
  4. <comp>
  5. <text class="text-style" slot="second">插入第二个插槽中</text>
  6. <text class="text-style" slot="first">插入第一个插槽中</text>
  7. </comp>
  8. </div>
说明:
name 和 slot 属性不支持绑定动态数据。

生命周期定义

说明:
从API Version 5 开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。

我们为自定义组件提供了一系列生命周期回调方法,便于开发者管理自定义组件的内部逻辑。生命周期主要包括:onInit,onAttached,onDetached,onLayoutReady,onDestroy,onShow和onHide。下面我们依次介绍一下各个生命周期回调的时机。

示例

  1. <!-- comp.hml -->
  2. <div class="item">
  3. <text class="text-style">{{ value }}</text>
  4. </div>
  1. //comp.js
  2. export default {
  3. data: {
  4. value: "组件创建"
  5. },
  6. onInit() {
  7. console.log("组件创建")
  8. },
  9. onAttached() {
  10. this.value = "组件挂载",
  11. console.log("组件挂载")
  12. },
  13. onShow() {
  14. console.log("Page显示")
  15. },
  16. onHide() {
  17. console.log("Page隐藏")
  18. }
  19. }

动态创建组件

提供在页面中动态添加组件,并为动态添加的组件设置属性与样式的能力。

说明:
从API version 8开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。

createElement

createElement(tag: string): Element

创建一个组件对象。

参数:

let newImage = dom.createElement('image')

setAttribute

setAttribute(name: string, value: string): void

动态设置组件的属性。

参数:

newImage.setAttribute('src', 'common/testImage.jpg')

addChild

addChild(child: Element): void

将动态创建的组件添加到父组件当中。

参数:

newDiv.addChild(newImage)

示例

  1. <!-- xxx.hml -->
  2. <div class="container">
  3. <div id="parentDiv" class="parent"></div>
  4. <button onclick="appendDiv" class="btn">动态创建div</button>
  5. <button onclick="appendImage" class="btn">动态创建image到创建的div中</button>
  6. </div>
  1. /* xxx.css */
  2. .container {
  3. flex-direction: column;
  4. align-items: center;
  5. width: 100%;
  6. }
  7. .parent {
  8. flex-direction: column;
  9. }
  10. .btn {
  11. width: 70%;
  12. height: 60px;
  13. margin: 15px;
  14. }
  1. // xxx.js
  2. let newDiv = null
  3. let newImage = null
  4. export default {
  5. appendDiv() {
  6. let parent = this.$element('parentDiv')
  7. newDiv = dom.createElement('div')
  8. newDiv.setStyle('width', '150px')
  9. newDiv.setStyle('height', '150px')
  10. newDiv.setStyle('backgroundColor', '#AEEEEE')
  11. newDiv.setStyle('marginTop', '15px')
  12. parent.addChild(newDiv)
  13. },
  14. appendImage() {
  15. newImage = dom.createElement('image')
  16. newImage.setAttribute('src', 'common/testImage.jpg')
  17. newImage.setStyle('width', '120px')
  18. newImage.setStyle('height', '120px')
  19. newDiv.addChild(newImage)
  20. }
  21. }

数据类型说明

长度类型

颜色类型

表1 当前支持的颜色枚举

3.渲染控制

ArkTS也提供了渲染控制的能力。条件渲染可根据应用的不同状态,渲染对应状态下的UI内容。循环渲染可从数据源中迭代获取数据,并在每次迭代过程中创建相应的组件。

条件渲染

使用if/else进行条件渲染。

说明:
if/else条件语句可以使用状态变量。
使用if/else可以使子组件的渲染依赖条件语句。
必须在容器组件内使用。
某些容器组件限制子组件的类型或数量,将if/else用于这些组件内时,这些限制将同样应用于if/else语句内创建的组件。例如,Grid容器组件的子组件仅支持GridItem组件,在Grid内使用if/else时,则if/else语句内也仅允许使用GridItem组件。
  1. Column() {
  2. if (this.count < 0) {
  3. Text('count is negative').fontSize(14)
  4. } else if (this.count % 2 === 0) {
  5. Text('count is even').fontSize(14)
  6. } else {
  7. Text('count is odd').fontSize(14)
  8. }
  9. }

循环渲染

通过循环渲染(ForEach)从数组中获取数据,并为每个数据项创建相应的组件,可减少代码复杂度。

  1. ForEach(
  2. arr: any[],
  3. itemGenerator: (item: any, index?: number) => void,
  4. keyGenerator?: (item: any, index?: number) => string
  5. )

参数:

说明:
ForEach必须在容器组件内使用。
生成的子组件应当是允许包含在ForEach父容器组件中的子组件。
允许子组件生成器函数中包含if/else条件渲染,同时也允许ForEach包含在if/else条件渲染语句中。
itemGenerator函数的调用顺序不一定和数组中的数据项相同,在开发过程中不要假设itemGenerator和keyGenerator函数是否执行及其执行顺序。例如,以下示例可能无法正常工作:
  1. ForEach(anArray.map((item1, index1) => { return { i: index1 + 1, data: item1 }; }),
  2. item => Text(`${item.i}. item.data.label`),
  3. item => item.data.id.toString())

示例

  1. // xxx.ets
  2. @Entry
  3. @Component
  4. struct MyComponent {
  5. @State arr: number[] = [10, 20, 30]
  6. build() {
  7. Column({ space: 5 }) {
  8. Button('Reverse Array')
  9. .onClick(() => {
  10. this.arr.reverse()
  11. })
  12. ForEach(this.arr, (item: number) => {
  13. Text(`item value: ${item}`).fontSize(18)
  14. Divider().strokeWidth(2)
  15. }, (item: number) => item.toString())
  16. }
  17. }
  18. }

数据懒加载

通过数据懒加载(LazyForEach)从提供的数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。

  1. LazyForEach(
  2. dataSource: IDataSource,
  3. itemGenerator: (item: any) => void,
  4. keyGenerator?: (item: any) => string
  5. ): void
  6. interface IDataSource {
  7. totalCount(): number;
  8. getData(index: number): any;
  9. registerDataChangeListener(listener: DataChangeListener): void;
  10. unregisterDataChangeListener(listener: DataChangeListener): void;
  11. }
  12. interface DataChangeListener {
  13. onDataReloaded(): void;
  14. onDataAdd(index: number): void;
  15. onDataMove(from: number, to: number): void;
  16. onDataDelete(index: number): void;
  17. onDataChange(index: number): void;
  18. }

参数:

IDataSource类型说明

DataChangeListener类型说明

示例

  1. // xxx.ets
  2. class BasicDataSource implements IDataSource {
  3. private listeners: DataChangeListener[] = []
  4. public totalCount(): number {
  5. return 0
  6. }
  7. public getData(index: number): any {
  8. return undefined
  9. }
  10. registerDataChangeListener(listener: DataChangeListener): void {
  11. if (this.listeners.indexOf(listener) < 0) {
  12. console.info('add listener')
  13. this.listeners.push(listener)
  14. }
  15. }
  16. unregisterDataChangeListener(listener: DataChangeListener): void {
  17. const pos = this.listeners.indexOf(listener);
  18. if (pos >= 0) {
  19. console.info('remove listener')
  20. this.listeners.splice(pos, 1)
  21. }
  22. }
  23. notifyDataReload(): void {
  24. this.listeners.forEach(listener => {
  25. listener.onDataReloaded()
  26. })
  27. }
  28. notifyDataAdd(index: number): void {
  29. this.listeners.forEach(listener => {
  30. listener.onDataAdd(index)
  31. })
  32. }
  33. notifyDataChange(index: number): void {
  34. this.listeners.forEach(listener => {
  35. listener.onDataChange(index)
  36. })
  37. }
  38. notifyDataDelete(index: number): void {
  39. this.listeners.forEach(listener => {
  40. listener.onDataDelete(index)
  41. })
  42. }
  43. notifyDataMove(from: number, to: number): void {
  44. this.listeners.forEach(listener => {
  45. listener.onDataMove(from, to)
  46. })
  47. }
  48. }
  49. class MyDataSource extends BasicDataSource {
  50. // 初始化数据列表
  51. private dataArray: string[] = ['/path/image0.png', '/path/image1.png', '/path/image2.png', '/path/image3.png']
  52. public totalCount(): number {
  53. return this.dataArray.length
  54. }
  55. public getData(index: number): any {
  56. return this.dataArray[index]
  57. }
  58. public addData(index: number, data: string): void {
  59. this.dataArray.splice(index, 0, data)
  60. this.notifyDataAdd(index)
  61. }
  62. public pushData(data: string): void {
  63. this.dataArray.push(data)
  64. this.notifyDataAdd(this.dataArray.length - 1)
  65. }
  66. }
  67. @Entry
  68. @Component
  69. struct MyComponent {
  70. private data: MyDataSource = new MyDataSource()
  71. build() {
  72. List({ space: 3 }) {
  73. LazyForEach(this.data, (item: string) => {
  74. ListItem() {
  75. Row() {
  76. Image(item).width(50).height(50)
  77. Text(item).fontSize(20).margin({ left: 10 })
  78. }.margin({ left: 10, right: 10 })
  79. }
  80. .onClick(() => {
  81. // 每点击一次列表项,数据增加一项
  82. this.data.pushData('/path/image' + this.data.totalCount() + '.png')
  83. })
  84. }, item => item)
  85. }
  86. }
  87. }
说明:
LazyForEach必须在容器组件内使用,目前仅有List、Grid以及Swiper组件支持数据懒加载(即只加载可视部分以及其前后少量数据用于缓冲),其他组件仍然是一次性加载所有的数据。
LazyForEach在每次迭代中,必须创建且只允许创建一个子组件。
生成的子组件必须是允许包含在LazyForEach父容器组件中的子组件。
允许LazyForEach包含在if/else条件渲染语句中。
为了高性能渲染,通过DataChangeListener对象的onDataChange方法来更新UI时,仅当itemGenerator中创建的子组件内使用了状态变量时,才会触发组件刷新。
itemGenerator函数的调用顺序不一定和数据源中的数据项相同,在开发过程中不要假设itemGenerator和keyGenerator函数是否执行及其执行顺序。例如,以下示例可能无法正常工作:
  1. LazyForEach(dataSource,
  2. item => Text(`${item.i}. item.data.label`),
  3. item => item.data.id.toString())

4.详细文档请见下链接

链接

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/243258
推荐阅读
相关标签
  

闽ICP备14008679号