鸿蒙应用开发从入门到入行
第五天 - 组件化开发思想开发鸿蒙案例(详解父子组件传值)
导读:在本篇文章里,您将掌握组件化开发、组件传值等相关知识。并能彻底弄懂鸿蒙父子组件数据的同步机制。本篇干货很多,特别是有些关于组件的细节,需要好好掌握。
本次整体学习目标介绍
最近比较忙,不过好在本文也是紧赶慢赶的弄出来了。
话不多说,我们先回顾一下我们需要做的案例
我们发现,这是一个综合性比较强的案例,涉及了布局、状态切换、列表渲染、数据新增、侧滑删除等功能。非常适合入门时的综合练手。
接下来,我们分析一下这个案例布局大致的划分
Progress的使用
咱们本次案例中需要用到一个进度缓,如上图
这种效果
ArkUI
已为我们提供了内置组件,即Progress
-
使用方法
Progress( { value: 当前值, total: 总值 } )
-
例
Progress( { value: 4, total: 10 } ) // 总量10,已完成4
默认情况下,Progress产生的是水平进度条,因此,上述代码会得到如下图效果
-
一些细节:
默认情况下,value为0,total为100
当Progress的宽度大于或等于高度时水平显示(如上图)
当Progress的宽度小于高度时(不包含等于,必须小于高度)垂直显示(如下图)
-
type参数
-
用法
Progress( { value: 5, total: 10, type: ProgressType.Ring } )
通过使用Progress传入type属性,可以修改Progress的样式
-
该参数需要传入
ProgressType
类型的枚举,一共有5种样式,分别如下ProgressType.Linear:线性样式,默认值。样式为上边两张图效果。
ProgressType.Ring:环形样式(无刻度),样式如下
-
- ProgressType.ScaleRing:环形样式(有刻度),样式如下
- ProgressType.Eclipse:圆形样式
- 和ProgressType.Capsule:胶囊样式(通过宽高设置水平或水平样式,参照ProgressType.Linear)
-
修改颜色,默认情况下,进度条底色为灰色,前景色(进度值的色)为渐变蓝色。如果需要改,可以分别通过
backgroundColor
(背景色)与color
(前景)改Progress({ value: 3, total: 10, type: ProgressType.Ring }) .width(80) .height(80) .backgroundColor(Color.Red) .color(Color.Pink)
- 这里是把背景改为红,进度值为粉色,如下图
组件化开发
- 组件:组成页面的一部分
- 组件化开发:把页面的每一部分当成一个组件,然后把这些组件像搭积木一样搭在一起即为组件化开发
- 组件化开发优势:代码分门别类,页面与逻辑内聚,方便阅读与维护、方便复用,等等,总之好多好多优点,这就不说了。
项目准备
- 我们来看看,这个年度待办案例,我们本次分几个区域(几个组件)
通过上图发现,我们需要三大区域,分别对应
头部区(统计)
、输入区
、列表展现区
,因此新建三个组件,然后集中到Index.ets
来用-
步骤
新建项目,过程略
把侧滑显示的删除图标放到
entry/src/main/resources/base/media
文件夹里(点我下载)在
pages
同级目录,新建view
文件夹-
在
view
文件夹,新建三个ets
文件,分别起名叫TodoHeader
、TodoInput
、TodoMain
,里面,每个组件里放一个Text
作为暂时的显示,且导出这个组件,这里仅贴TodoHeader代码,其他两个组件类似@Component export struct TodoHeader { build () { Text('TodoHeader') } }
-
然后来到
pages/Index.ets
,导入这三个组件,并依次写到Column
里(因为这三个组件如上图所示,就是从上到下依次排列)- 技巧:上篇说过,不用导入,只要在组件写了
export
的情况下,直接写组件名出提示后按回车,会自动生成导入代码 - Index.ets代码如下
import { TodoHeader } from '../view/TodoHeader' import { TodoInput } from '../view/TodoInput' import { TodoMain } from '../view/TodoMain' @Entry @Component struct Index { build() { Column({ space: 20 }) { TodoHeader() TodoInput() TodoMain() } .height('100%') .width('100%') .backgroundColor('#f2f3f5') } }
- 技巧:上篇说过,不用导入,只要在组件写了
注意:根据效果图发现整个页面背景是灰色的,因此记得给整个Column加背景色,每个区域之间有间距,因此加space
至此,项目准备工作完成
TodoHeader - 布局实现
- 分析布局如图
-
根据上图很明显能发现,就是一个Row(水平布局),里面一个Text显示标题,一个Stack放层叠布局(里面再放Progress与Text,具体后面说)
- 记得给Row宽度、高度与背景色、Text要加粗、内容居中等
-
基于此,代码如下
@Component export struct TodoHeader { build() { Row() { Text('今年目标') .fontSize(30) .fontWeight(FontWeight.Bold) .margin({ right: 20 }) Stack() { Progress({ value: 0, total: 10, type: ProgressType.Ring }) .width(80) .height(80) Text(`0 / 10`) } } .width('100%') .height(110) .justifyContent(FlexAlign.Center) .backgroundColor(Color.White) } }
这里其他属性都很容易,如果有不懂的可以评论区留言
主要解释下
Stack
环形进度条那里
如上图解释的:这里其实有两个组件
Progress
用来显示环形进度条,但是它没有文字,所以还要搭一个Text
而如果不加堆叠,则他们会并排显示,如下图
- 所以此时我们是期望能让
3 / 10
这个Text
能叠在Progress
上面,所以这种层叠效果,需要套一个Stack
来实现(注意,默认情况下后面的在最上层,因此Text必须放在最后。除此外,默认都是居中堆叠,所以刚好Text就在环形中间了)
- 注意:本代码里的宽高颜色都可以由各位根据预览效果自行设置,不必强求与猫林老师一致
TodoInput - 布局实现
如上图所示,Row里放
TextInput
即可,记得让TextInput不用铺满Row。因为将来必然会需要拿到输入框里的内容,因此声明个变量,并与之双向绑定-
代码如下
@Component export struct TodoInput { @State newFlag: string = '' build() { Row() { TextInput({ placeholder: '请输入新目标', text: $$this.newFlag }) .width('90%') } .width('100%') .height(80) .backgroundColor(Color.White) .justifyContent(FlexAlign.Center) } }
TodoMain - 布局实现
-
发现这个区域就是一堆上一篇文章里教大家封装的组件,直接拿来用即可
- 注:上一篇里猫林老师不小心起错名字。其实规范的名字应该叫
TodoItem
,上一篇写成了ToDoItem
,这里正好勘误,用更规范的写法
- 注:上一篇里猫林老师不小心起错名字。其实规范的名字应该叫
-
步骤
-
来到
view
新建TodoItem.ets
文件,然后写如下代码(分析过程略,见上篇)@Component export struct TodoItem { build() { Row() { Checkbox() .margin({ left: 20, right: 20 }) Text('跟猫林学鸿蒙') } .width('100%') .height(40) .backgroundColor(Color.White) .borderRadius(20) } }
-
然后来到
TodoMain
组件,进行导入与使用,为了暂时能看到一堆内容,我们用ForEach
先循环生成10个,TodoMain
代码如下import { TodoItem } from './TodoItem' @Component export struct TodoMain { @State todoList: number[] = [1, 2, 3, 4, 5, 6, 7] build() { Column({ space: 10 }) { ForEach(this.todoList, (item: number) => { TodoItem() }) } .width('100%') } }
- 这里
TodoMain
的根容器是Column
,因为它需要一行一行从上到下来显示
- 这里
-
初始数据与说明
-
初始数据如下
class TodoModel { text: string = '' finished: boolean = false } @State totalFlags: Array<TodoModel> = [ { text: "月入5万", finished: false }, { text: "中彩票500万", finished: false }, { text: "找个富婆", finished: false }, { text: "买套别墅", finished: false }, { text: "改掉爱做梦的习惯", finished: false }, ];
-
解释
- 每个新年目标都是个对象,有两种属性:text(目标文字)、finished(是否完成)
- 以下是对语法的解释,会TS的可跳过下面这三段
- 因为ArkTS是一种类型严谨的语言,因此需要对这种对象做一个类型定义,即声明一个类叫TodoModel,它里面有两个这样的属性
- totalFlags即为这种对象类型的数组,例如Array<number>代表数组每个元素都是数值类型的数组,所以上面写的Array<TodoModel>代表数组每个元素都是TodoModel类型的数组
- 数组也可以简写为
number[]
、本例中的Array<TodoModel>
可简写为TodoModel[]
按照官方的编码规范指导,这种项目中用到的数据对应的类,要写到
viewsmodel
文件夹(与pages平级),我们新建好文件夹后,也在此文件夹再新建TodoModel.ets
文件,声明这个类,并导出
- 文件内代码如下
export class TodoModel {
text: string = ''
finished: boolean = false
}
-
根据功能分析,本案例的年度目标必然是一个数组,且可对其增、删、改。但是这个数组在本案例中三大组件里都需要用:
- TodoHeader 需要数组来统计总目标和已完成目标
- TodoInput 需要给数组添加新内容
- TodoMain 需要展示数组内容,并且将来侧滑时需要能删除数组内容
-
基于上面需求,提问:数组应该放在哪?
- 没错,应该放到他们共同的父组件,本案例即为Index.ets里。
- 来到
Index.ets
,导入上面声明的类,并声明一个数组
...... import { TodoModel } from '../viewmodel/TodoModel' @Entry @Component struct Index { @State totalFlags: Array<TodoModel> = [ { text: "月入5万", finished: false }, { text: "中彩票500万", finished: false }, { text: "找个富婆", finished: false }, { text: "买套别墅", finished: false }, { text: "改掉爱做梦的习惯", finished: false }, ]; build() { ...... } }
组件传参 - 父传子
-
此时数据有了,但还没交给
TodoMain
去显示,此时相当于要把Index
里的数据给TodoMain
,且因为Index
是父组件,TodoMain
是子组件,因此这种数据传递方式叫父传子,即把父的数据传递给子组件- 注:本章节主要是为了学习语法,最终部分代码不会出现于
年度待办案例
,因此此时先把TodoMain
与TodoItem
代码做一个备份。然后再开始练下面语法
- 注:本章节主要是为了学习语法,最终部分代码不会出现于
-
语法步骤:
- 子里声明一个成员变量
- 父里使用组件时,在小括号里传入
例如,本案例中我们有
TodoMain
和TodoItem
,因为TodoMain
包含了TodoItem
。所以TodoMain
是父TodoItem
是子。我们就用这两个组件试试父传子
-
代码步骤:
-
TodoItem
里声明一个变量叫name
,并在Text
里使用,代码如下export struct TodoItem { // 声明个成员变量,等待父传,注意:此时没加任意装饰器 name: string = '' build() { ....... Text(this.name) } }
-
TodoMain
里声明一个变量name,并传递给TodoItem
@Component export struct TodoMain { ....... @State name: string = 'abc' build() { Column({ space: 10 }) { ........ ForEach(this.todoList, (item: number) => { // 这里是传参,把父的name传递给了子里的name TodoItem({ name: this.name }) }) } .width('100%') } }
此时会发现,正因为把父的
name
,也即数据为abc
,传递给了子,所以此时TodoItem
显示的即为abc
,如下图
-
注意,此时虽能成功父传子,但是父的数据一旦改变,子并不会跟着改变
-
我们此时可以测试一下,在
TodoMain
里,添加一个Button,并在Button里修改掉name
的值,如下代码export struct TodoMain { .... @State name: string = 'abc' build() { Column({ space: 10 }) { Button('我改').onClick((event: ClickEvent) => { this.name = 'Good猫林' }) ......... } ...... } }
- 点击按钮后会发现,子组件也即
TodoItem
上没有任何变化
- 点击按钮后会发现,子组件也即
-
如果希望实现父的数据改变,子的数据能随着改变,需要在子的变量前加@Prop装饰器
-
修改
TodoItem
里的name,前面加上@Prop,再来点击按钮看变化export struct TodoItem { // 此时加了@Prop修饰 @Prop name: string = '' build() { Row() { Checkbox() .margin({ left: 20, right: 20 }) Text(this.name) } ....... } }
此时点击按钮,让父的数据改变,子里的也跟着变
-
- 到目前,我们已经学了两个用来修饰成员变量的装饰器,分别是
@State
、@Prop
,我们对它总结一下- @State: 主要是装饰给组件自己使用的数据,效果:能让成员变量的值改变后,界面也能刷新
- @Prop: 主要是用在作为子组件时,用来装饰由父传递过来的数据,效果:能让父的数据改变子也能接收到
- 注意:在
ArkTS
中,即使父传递的是引用类型的数据,若不加@Prop
修饰,一样会导致父的数据改变子里不会变,同学们有兴趣可以自行测试
- 注意:在
组件传参 - 父传子双向同步
上面我们讲到,子里的成员变量加
@Prop
后,即可让父的数据改变,子随之改变,也即父的数据自动同步到子。-
但是,目前无法实现子同步到父,也即子里改变了这个父传进来的数据,子里自身能改变,但是父的无法改变。也即Vue框架里的
单向数据流
例:在
TodoMain
里用一个Text显示name的值,并在TodoItem
里给Row
加点击事件并修改name的值,我们可观察效果-
TodoMain代码
export struct TodoMain { .... @State name: string = 'abc' build() { Column({ space: 10 }) { Button('我改').onClick((event: ClickEvent) => { this.name = 'Good猫林' }) Row() { // 显示name Text(this.name) } ......... } ...... } }
-
TodoItem代码
export struct TodoItem { // 此时加了@Prop修饰 @Prop name: string = '' build() { Row() { ...... } .onClick(() => { this.name = '学鸿蒙' }) ....... } }
-
发现当我们点击
TodoItem
时,TodoItem
自身的name数据了学鸿蒙
,但是父里的还是abc
,如图
如果希望实现:父改了数据,自动同步到子。以及子改了数据,也同步到父,需要把子里的
@Prop
修改成@Link
-
注意:如果用
@Link
修饰了成员变量,则成员变量不可初始化,例export struct TodoItem { // 此时加了@Link,不用初始化 @Link name: string build() { ....... } }
虽然仅仅只是把
@Prop
改成了@Link
,且把初始化值的部分删了,但此时已经完成了双向同步,我们来点击一下Row测试一下效果,会发现如下图所示,子和父都变成了学鸿蒙
- 总结
@Prop
与@Link
- 相同点:
- 都是用在子组件,用来接收父传递过来的数据,
- 都可以实现父改变数据后同步给子
- 不同点:
- 初始化值不同:
- @Prop需要初始化值,相当于给默认值。可以实现,父如果传了就用父的数据,如果没传则用默认值
- @Link不能初始化,相当于必须要父传递数据了才有数据
- 同步给父不同
- @Prop修饰的数据,子里改变了不会同步给父
- @Link修饰的数据,子里改变了会同步给父
- 初始化值不同:
- 相同点:
年度目标案例 - 数据展示
回到我们之前最初的需求:要把
Index
里的数据给TodoMain
,经历了上面组件传参的学习后,我们能很快完成。-
步骤:
注意:先把之前备份的TodoMain与TodoItem恢复
-
TodoMain
里,声明成员变量接收Index
传递过来的数组,之前备份的文件里其实已经声明过,只不过是number类型的,改成TodoModel
即可,代码如下@Link todoList: TodoModel[] = []
- 解释为什么用
@Link
修饰符- 因为在
TodoMain
里将来需要改变数组(例如侧滑删除等),需要同步给父组件(即Index),因此用@Link
- 因为在
- 解释为什么用
-
Index
里传递(这里Index的数据在上面初始数据与说明
章节已声明过)TodoMain({ todoList: this.totalFlags })
此时已经把数组从
Index
传递到TodoMain
,但是TodoMain
里又需要遍历这个数组去产生TodoItem
,并且需要把数组每一项交给TodoItem
去渲染,所以这里又要父传子,思路示意图如下
-
来到
TodoItem
,声明一个TodoModel
的对象,用来接收数组每一项,并且把item
里的finished
属性绑定给Checkbox
,text
属性绑定给Text
去显示import { TodoModel } from '../viewmodel/TodoModel' @Component export struct TodoItem { @Prop item: TodoModel = new TodoModel() // 本次代码 build() { Row() { Checkbox() .select(this.item.finished) // 本次代码 .margin({ left: 20, right: 20 }) Text(this.item.text) // 本次代码 } .width('100%') .height(40) .backgroundColor(Color.White) .borderRadius(20) } }
-
TodoMain
里使用ForEach
渲染import { TodoItem } from './TodoItem' import { TodoModel } from '../viewmodel/TodoModel' @Component export struct TodoMain { @Link todoList: TodoModel[] build() { Column({ space: 10 }) { // 本次代码 ForEach(this.todoList, (item: TodoModel, index: number) => { // 这里用了ES6简写,完整写法是 TodoItem({ item: item })代表把ForEach里的item传递给TodoItem需要的item TodoItem({ item }) }) // 以上是本次代码改动 } .width('100%') } }
此时便完成了数组的渲染
年度目标案例 - 目标完成打勾
- 需求如下:
-
根据上面数据说明,打勾与否需要与每一项的
finished
属性绑定,因此先来到TodoItem
给Checkbox
的select
属性做双向绑定Checkbox() .select($$this.item.finished)
- 注:
- Checkbox的
select
属性方法是用来设置它是否打勾的。传true代表打勾,传false代表不打勾 - 这里我们除了要数据能影响Checkbox以外,也需要当我们操作组件后结果能影响到数据,因此加
$$
做双向绑定
- Checkbox的
- 注:
-
然后根据
finished
的值,做不同的处理如果是true,给文字加删除线,否则不加任何线
-
如果是true,给文字加opacity为半透明,否则不透明
- 解释:通过需求图可以看到,要让文字和删除线都变灰,最快的方式是给整个Text加透明度,配合白色底就会呈现灰色。而如果单独知识改文字颜色也即
fontColor
只会让文字变灰,删除线不变,还得在decoration
里加color才有用,不方便
- 解释:通过需求图可以看到,要让文字和删除线都变灰,最快的方式是给整个Text加透明度,配合白色底就会呈现灰色。而如果单独知识改文字颜色也即
-
代码如下
Text(this.item.text)// 本次代码 .opacity(this.item.finished ? 0.4 : 1) .decoration({ type: this.item.finished ? TextDecorationType.LineThrough : TextDecorationType.None })
- 注:decoration就是Text设置文字线条的样式,可以设置删除线、下划线等,具体可查官方文档
插播 - 目前鸿蒙开发的缺陷
目前虽然实现了数据展现与目标完成的打勾,但此时存在数据传递的问题
通过最终效果图我们知道,我们需要统计出已完成的目标数量展现到进度条里。但此时,我们在
TodoItem
里打勾了后,finished的值已经改变(通过界面有变化能证明),但是没有传入到父,导致父里的finished还是false-
例
-
我们在父里(TodoMain)里写一个Row输出数组里下标0的元素的finished
....... build() { Column({ space: 10 }) { Row() { Text(this.todoList[0].finished + '') } .......... } } .....
注:这里的
+ ''
是为了把finished
转为字符串类型,因为Text
只能用字符串然后给第一项打勾,会发现如下图效果
-
- 大家思考一下:为什么会这样呢?
- 请思考
- 请思考
- 请思考
- 请思考
- 请思考
- ..........
- 没错,就是因为子里用的是
@Prop
,我们上面说过,@Prop
只能实现父数据同步到子,子里变了无法同步到父,此时就是这原因导致
-
如何解决?
- 相信同学们都能想到用
@Link
,父子双向同步。但此时,鸿蒙的缺陷体现出来了!当你把TodoItem
里的item
改成@Link
装饰后,惊讶地发现:在TodoMain
里报错了!如下图
- 相信同学们都能想到用
原因:语法限制,@Link只能接收父组件里声明的第一层成员变量
-
什么叫第一层?
- 就好比一个数组,数组里全是对象。对于数组而言即为第一层,数组里的每个对象称之为第二层,以此类推
- 再好比一个嵌套对象。即对象里有个属性又是对象,那么外层的称之为第一层,里面的属性即为第二层,以此类推
所以上述报错里写的
item
相当于就是数组里的对象,也即第二层,所以报错
- 出现这个语法限制的根本原因是:目前的鸿蒙开发中,默认情况下无法监听到第二层的改动。而@Link又要实现双向同步,你都无法监听到改动,又如何完成双向同步呢?
- 所以鸿蒙也给了解决方案:使用
@Observed
加@ObjectLink
来解决。但是,猫林老师这里不打算讲它。因为这个解决方案其实用起来也很麻烦繁琐,非常不人性化。- 有兴趣的同学可以自行去学习Observed与ObjectLink,猫林老师这里只讲最实用的技术,帮助你更快开发鸿蒙!
- 题外话,从API12开始,鸿蒙提供了
@ObservedV2
,力求解决监听这种监听属性的问题。但猫林老师尝鲜过,也不是很方便,这里赞不推荐 - 题外话2:不要一听猫林老师吐槽一句鸿蒙开发中存在这种不太方便的点,就觉得鸿蒙不行。任何语言刚推出时总有些坑或者不太方便的地方需要等待后续更新。当初苹果的Swift刚开始推出时也有许多地方有待完善,苹果也是慢慢更新使之越来越好。所以大家要保持好信心,未来可期。而且,华为官方也确实不断的对鸿蒙开发做着改进,就像上面提到的
@ObservedV2
。所以鸿蒙开发未来必然会越来越方便,越来越容易好用。可是到那时越容易就会越多人涌进,但是不要怕。你们在此刻开始进入鸿蒙的人,都算得上是鸿蒙的元老级程序员。(毕竟现在连纯血鸿蒙的正式版都还没发布,仅仅是测试版呢),所以对你们,更是抓住风口的弄潮儿,必将成为吃到红利的一波人!
待办列表 - 解决缺陷 - 实现打勾改动同步给父
-
那,猫林老师这里怎么解决上述缺陷呢?首先,因为
@Link
目前不能用,那咱们就把它换回@Prop
先..... @Component export struct TodoItem { @Prop item: TodoModel ..... }
可是
@Prop
又确实无法让父的数据同步改变,该怎么办呢?
既然子里无法改动到父,那就换个思路。让父,自己改!!
-
整体思路是:
让父提供一个修改数据的方法, 子里要修改时调用父的方法即可修改!
如图
-
实现:
-
来到
TodoMain
,声明一个方法如下changeStatus(item: TodoModel, index: number) { this.todoList[index] = { text: item.text, finished: !item.finished } }
- 解释:
- 本方法需要传入被点的
item
以及被点的item
的索引 - 通过索引的方式改掉数组中这一项,文字不变,但是完成状态取反即可
- 这时候可能有老铁有疑问:
- 为什么不直接 item = !item.finished
- 还是那个问题:目前不支持监听第二层数据改变,直接改item还是第二层。
- 但数组是第一层,因此你用
数组[索引]
的方式,就是在改第一层数据,这是能被监听到的
- 本方法需要传入被点的
- 解释:
-
此时需要把这个方法传递给
TodoItem
,因此TodoItem
需要声明一个成员方法来接收export struct TodoItem { ...... onChange: () => void = () => {} ..... }
- 解释:
- 方法名叫
onChange
,它的类型是一个无参数无返回值的函数,初始化值是一个空函数 - 一般情况下,这种由外界传入的方法不需要加装饰器
- 方法名叫
- 解释:
-
然后给
Select
组件加onChange
事件,这个事件是当Select
发生勾选状态改变就会调用的事件,在里面调用传入的onChange
方法Checkbox() .select(this.item.finished) .margin({ left: 20, right: 20 }) .onChange(() => { // 本次代码 this.onChange() })
-
回到
TodoMain
做方法传递:此时调用TodoItem除了之前要传入的item,现在还要多一个onChange
ForEach(this.todoList, (item: TodoModel, index: number) => { // 此时调用TodoItem TodoItem({ item, onChange: () => { this.changeStatus(item, index) } }) })
- 解释参数:item即为当前变动的数据,index即为当前数据对应的下标(都是changeStatus需要用到的内容)
- 注意看:这里我没有直接把
this.changeStatus
这个方法传递给onChange
,而是声明了一个新的箭头函数,只不过在箭头函数里的函数体里调用了this.changeStatus
,这么做的原因是this.changeStatus
方法里有this.todoList
这样的代码,我们都知道this是指当前环境上下文,它在TodoMain
里,就代表TodoMain
这个上下文,所以它修改它的todoList没毛病。但如果你是直接把this.changeStatus
传递给onChange
,那它相当于是在todoItem
里调用,同样的this也会变成todoItem
上下文,此时它是没有todoList
数组的,所以这里利用箭头函数保留当前上下文的特点,在todoMain
里用箭头函数再包一层,即可保证this依然指向todoMain
------- 这段请可能理解,因为文字有其局限性,实在不理解等以后猫林老师出视频教程再来看 - 附:有懂JS的同学可能会问:那可以
onChange: this.changeStatus.bind(.....)
这样的方式传递吗?似乎也能绑定当前this指向呀?- 答:不能!ArkTS中不允许使用
call
、apply
、bind
- 具体可查看当前最新文档,点我进入,打开页面后搜:不支持Function.bind
- 答:不能!ArkTS中不允许使用
-
至此,状态改变同步给父已完成。
鉴于篇幅关系以及一篇文章颗粒度太大也影响大家阅读。所以
年度目标
这个案例的剩余部分我在下一篇文章讲完,其中下一篇也会以需求驱动的方式为大家讲到很多干货新知识,请拭目以待!
总结今日内容
- 组件
- 组件化开发思想
- 父子组件数据传递与同步
- 本篇干活与细节略多,需要多认真思考学习。
- 忠告:我们是以需求驱动学习知识点。特别是本篇含有部分需要理解与实操的内容。鸿蒙零基础的同学,一定要好好的跟着敲本案例代码才能理解
课后练习
- 判断题
- 一个Progress的宽度是100,高度是150,此时环形图一定是垂直方向的线性样式滚动条
互动简答题
- 思考:本案例中,最终
TodoItem
里的数据打勾变化后(完成状态变化),TodoMain
已经能成功收到改动了。那么它的父组件,最早持有数组的Index
有收到改动吗?请说出你的理由,打在评论区
判断题答案
- 错。原因自己思考,实在不懂可以评论区留言找一起学的同学帮助