【鸿蒙】HarmonyOS NEXT星河入门到实战8-自定义组件-组件通信

目录

1、模块化语法

1.1 模块化基本认知

1.2 默认导出和导入

1.2.1 在ets下新建tools目录

1.2.2 在tools下新建moduls.ets文件

1.2.3 index.ets

1.3 按需导出和导入

1.4 全部导入

2、自定义组件 -基础

2.1 自定义组件 - 基本使用

 2.2 自定义组件 -通用样式

2.2.1 ets下新建components文件夹

2.2.2 components新建HelloCom.ets

2.2.3 Index.ets

2.3 自定义组件 -成员函数变量

3、@BuilderParam

3.1 @BuilderParam 传递UI

3.2  多个 @BuilderParam 参数

4、 状态管理

4.1 状态管理概述

4.2 @ State 自己的状态

4.3 @Prop -父子单向

4.4 掘金评论案例

4.4.1 头部组件

4.4.1.1 在ets/components下新建InfoTop.ets

4.4.1.2 Index.ets

4.4.2 评论案例 - List列表组件的使用

4.4.2.1 List列表基本使用

 4.4.2.2 中间评论列表

4.4.2.2.1 在components下新增InfoItem.ets

4.4.2.2.2 Index .ets

4.4.3 底部评价

4.4.3.1 IconFont的使用

4.4.3.1.1 ets\fonts下存放下载的文件

4.4.3.1.2 注册字体&使用字体

4.4.3.2  在components下新增InfoBottom.ets

4.4.3.3  Index .ets

4.4.4 列表渲染

4.4.4.1 准备数据

4.4.4.2 列表渲染完成

4.4.4.3 点赞

4.4.4.4 添加评论

4.4.4.5 评论排序

4.5 @Link双向同步 

 4.6 @Provide \@Consume后代组件

4.7 @ Observed &@ObjectLink嵌套对象数组属性变化


前言:自定义组件-组件通信(模块化语法)、自定义组件、传递UI、状态管理

1、模块化语法

1.1 模块化基本认知

1.2 默认导出和导入

1.2.1 在ets下新建tools目录

 

1.2.2 在tools下新建moduls.ets文件

interface  Person {name: stringage: number
}let num: number = 10
let person: Person ={name: '春天的菠菜',age: 18
}
// 默认导出(导出一个值)
export  default num

1.2.3 index.ets

导入进来  import 导入进来新取的名称  from 路径(相对路径)

import window from '@ohos.window';
// 相对路径
import myNum from '../tools/moduls'@Extend(Text)
function textExtend(){.fontSize(20).fontWeight(700).backgroundColor(Color.Green).padding(10).margin(5)
}@Entry
@Component
struct Index {@State message: string = '@春天的菠菜';onPageShow(): void {window.getLastWindow(AppStorage.get("context"), (err, data) => {if (err.code) {console.error('Failed to get last window. Cause:' + JSON.stringify(err));return;}data.setFullScreen(true)});}build() {Column(){Text('来自tools/modlus 模块的值:'+ myNum).textExtend()}.width('100%').height(100)}
}

1.3 按需导出和导入

 tools下新建/module3.ets

// 按需导出
// 多个特性, 逐个 export 按需导出
// export let name1: string = '刘备'
// export let price: number = 9.98
// export let sayHi = () => {
//   console.log('打招呼')
// }let name1: string = '刘备'
let name2: string = '张飞'
let name3: string = '关羽'let price: number = 9.98
let price2: number = 10.1let sayHi = () => {console.log('打招呼')
}
let run = () => {console.log('跑步')
}// 一次性将多个特性, 进行导出
export {name1, name2, name3,price, price2,sayHi, run
}

import window from '@ohos.window';
// 相对路径
import myNum from '../tools/moduls'
import { name1, price, sayHi }from '../tools/module3'sayHi()@Extend(Text)
function textExtend(){.fontSize(20).fontWeight(700).backgroundColor(Color.Green).padding(10).margin(5)
}@Entry
@Component
struct Index {@State message: string = '@春天的菠菜';onPageShow(): void {window.getLastWindow(AppStorage.get("context"), (err, data) => {if (err.code) {console.error('Failed to get last window. Cause:' + JSON.stringify(err));return;}data.setFullScreen(true)});}build() {Column(){Text('来自tools/modlus 模块的值:'+ myNum).textExtend().margin(5)Text('来自tools/module3 模块的值:'+ name1 +'**' + price ).textExtend()}.width('100%').height(100)}
}

1.4 全部导入

2、自定义组件 -基础

2.1 自定义组件 - 基本使用

1、无@Entry 2、 struct 后面自定义名称

import window from '@ohos.window';@Extend(Text)
function textExtend(){.fontSize(20).fontWeight(700).backgroundColor(Color.Green).padding(10).margin(5)
}@Entry
@Component
struct Index {@State message: string = '@春天的菠菜';onPageShow(): void {window.getLastWindow(AppStorage.get("context"), (err, data) => {if (err.code) {console.error('Failed to get last window. Cause:' + JSON.stringify(err));return;}data.setFullScreen(true)});}build() {Column() {MyHeader()MyMain()MyFooter()}}
}@Component
struct MyCom {@State count: number = 1build() {Row() {Text(this.count.toString()).fontColor(Color.White).margin(10)Button('按钮').onClick(() => {this.count++})}}
}@Component
struct MyHeader {build() {Row() {Text('我是头部').fontColor(Color.White)}.width('100%').height(50).backgroundColor(Color.Brown)}
}@Component
struct MyMain {build() {Column() {// 将相同的业务逻辑, 封装成一个通用的组件MyCom()MyCom()MyCom()}.layoutWeight(1).width('100%').backgroundColor(Color.Gray)}
}@Component
struct MyFooter {build() {Row() {Text('我是底部')}.width('100%').height(50).backgroundColor(Color.Green)}
}

 2.2 自定义组件 -通用样式

2.2.1 ets下新建components文件夹

2.2.2 components新建HelloCom.ets

@Preview
@Component
export struct HelloCom {build() {Row() {Text('自定义组件')Button('按钮')}.width(200).height(50).backgroundColor(Color.Orange)}
}

2.2.3 Index.ets

import window from '@ohos.window';
import { HelloCom } from '../components/HelloCom'@Extend(Text)
function textExtend(){.fontSize(20).fontWeight(700).backgroundColor(Color.Green).padding(10).margin(5)
}@Entry
@Component
struct Index {@State message: string = '@春天的菠菜';onPageShow(): void {window.getLastWindow(AppStorage.get("context"), (err, data) => {if (err.code) {console.error('Failed to get last window. Cause:' + JSON.stringify(err));return;}data.setFullScreen(true)});}build() {Column() {HelloCom().width(250).height(60).backgroundColor(Color.Gray).onClick(() => {AlertDialog.show({message: '测试点击'})})}}
}

2.3 自定义组件 -成员函数变量

带=符号的可以外部传参

import window from '@ohos.window';@Entry
@Component
struct Index {@State message: string = '@春天的菠菜';onPageShow(): void {window.getLastWindow(AppStorage.get("context"), (err, data) => {if (err.code) {console.error('Failed to get last window. Cause:' + JSON.stringify(err));return;}data.setFullScreen(true)});}build() {Column() {MyPanel({title: '我的订单',extra: '全部订单',getMore() {AlertDialog.show({message: '点击了全部订单'})}})MyPanel({title: '小米有品众筹',extra: '七款众筹中',getMore() {AlertDialog.show({message: '点击了众筹'})}})}.width('100%').height('100%').backgroundColor('#ccc').padding(20)}
}@Component
struct MyPanel {// 成员变量 -数据title: string = '默认的大标题'extra: string = '查看更多'// 成员变量 -函数 可以传入覆盖getMore = () =>{AlertDialog.show({message: '查看更多'})}// 成员函数  --  不可以外部传入覆盖sayHi (){AlertDialog.show({message: '打招呼 你好'})
}build() {Column(){Row(){Text(this.title).fontSize(18)Text(this.extra).fontSize(18).onClick( () => {this.getMore()})}.width('100%').justifyContent(FlexAlign.SpaceBetween)Row(){Text('内容部分').fontSize(18)Button('按钮').onClick(() => {this.sayHi()})}.padding(20)}.width('100%').height(200).margin({bottom: 20}).borderRadius(10).padding(10).backgroundColor(Color.White)}
}

3、@BuilderParam

3.1 @BuilderParam 传递UI

 

import window from '@ohos.window';@Component
struct  SonCom {// 1、 定义构建函数@BuilderParam ContentBuilder: () => void = this.defaultBuilder@BuilderdefaultBuilder () {Text('默认的内容')}build() {//   2、 使用构建函数,构建结构Column(){this.ContentBuilder()}}
}@Entry
@Component
struct Index {@State message: string = '@春天的菠菜';onPageShow(): void {window.getLastWindow(AppStorage.get("context"), (err, data) => {if (err.code) {console.error('Failed to get last window. Cause:' + JSON.stringify(err));return;}data.setFullScreen(true)});}build() {Column() {SonCom() {Button('传入的结构')}}}
}

 

练习

import window from '@ohos.window';@Entry
@Component
struct Index {@State message: string = '@春天的菠菜';onPageShow(): void {window.getLastWindow(AppStorage.get("context"), (err, data) => {if (err.code) {console.error('Failed to get last window. Cause:' + JSON.stringify(err));return;}data.setFullScreen(true)});}build() {Column() {MyPanel({title: '我的订单',extra: '全部订单',getMore() {AlertDialog.show({message: '点击了全部订单'})}}) {Text('我是订单相关的文本')}MyPanel({title: '小米有品众筹',extra: '七款众筹中',getMore() {AlertDialog.show({message: '点击了众筹'})}}) {Button('我是小米众筹的按钮')}}.width('100%').height('100%').backgroundColor('#ccc').padding(20)}
}@Component
struct MyPanel {// 成员变量 -数据title: string = '默认的大标题'extra: string = '查看更多'// 成员变量 -函数 可以传入覆盖getMore = () =>{AlertDialog.show({message: '查看更多'})}// 成员函数  --  不可以外部传入覆盖sayHi (){AlertDialog.show({message: '打招呼 你好'})}// 1、 定义构建函数@BuilderParam ContentBuilder: () => void = this.defaultBuilder@BuilderdefaultBuilder () {Text('默认的内容')}build() {Column(){Row(){Text(this.title).fontSize(18)Text(this.extra).fontSize(18).onClick( () => {this.getMore()})}.width('100%').justifyContent(FlexAlign.SpaceBetween)Row(){//   这里结构不能写死,需要通过BUilderParam来进行构建this.ContentBuilder()}.padding(20)}.width('100%').height(200).margin({bottom: 20}).borderRadius(10).padding(10).backgroundColor(Color.White)}
}

3.2  多个 @BuilderParam 参数

import window from '@ohos.window';@Component
struct MyCard {@BuilderParam tBuilder: () => void = this.tDefaultBuilder@BuilderParam cBuilder: () => void = this.cDefaultBuilder@Builder tDefaultBuilder () {Text('我是默认的大标题')}@Builder cDefaultBuilder () {Text('我是默认的内容')}build() {// 卡片组件Column() {// 标题部分Row() {this.tBuilder()}.height(30).width('100%').border({ color: '#ccc', width: { bottom: 1 }}).padding({ left: 10 })// 内容部分Row() {this.cBuilder()}.width('100%').padding(10)}.width('100%').height(100).backgroundColor(Color.White).borderRadius(10).justifyContent(FlexAlign.Start)}
}@Entry
@Component
struct Index {@State message: string = '@春天的菠菜';onPageShow(): void {window.getLastWindow(AppStorage.get("context"), (err, data) => {if (err.code) {console.error('Failed to get last window. Cause:' + JSON.stringify(err));return;}data.setFullScreen(true)});}@Builder ftBuilder () {Text('我是传入的大标题结构')}@Builder fcBuilder () {Text('我是内容部分').margin(5)Text('我是内容部分').margin(5)Text('我是内容部分').margin(5)}build() {Column({ space: 10 }) {MyCard()MyCard({tBuilder: this.ftBuilder,cBuilder: this.fcBuilder})}.width('100%').height('100%').padding(20).backgroundColor('#ccc')}
}

4、 状态管理

4.1 状态管理概述

4.2 @ State 自己的状态

import window from '@ohos.window';interface Car {name: string
}
interface Person {name: stringcar: Car
}const obj: Person = {name: 'zs',car: {name: '小黄车'}
}
console.log('查看第一层属性', Object.keys(obj))@Entry
@Component
struct Index {onPageShow(): void {window.getLastWindow(AppStorage.get("context"), (err, data) => {if (err.code) {console.error('Failed to get last window. Cause:' + JSON.stringify(err));return;}data.setFullScreen(true)});}// 状态变量// 1. string number boolean 可以直接监视到变化@State message: string = 'hello world'// 2. 复杂类型 object class, 第一层随便改, 嵌套需要进行整个嵌套对象的替换@State person: Person = {name: 'jack',car: {name: '宝马车'}}build() {Column() {Text(this.message).fontSize(20)Button('改message').onClick(() => {this.message = '你好'})Text(JSON.stringify(this.person))Button('改person').onClick(() => {// this.person = {//   name: 'amy',//   car: {//     name: '保时捷'//   }// }// this.person.name = 'tony'// 如果不是对象的第一层属性, 修改时, 需要修改整个嵌套的对象// this.person.car.name = '小火车'// console.log('car name', this.person.car.name)this.person.car = {name: '老爷车'}})}}
}

 

4.3 @Prop -父子单向

import window from '@ohos.window';@Component
struct SonCom {// 保证父组件的数据变化了, 能够往下响应式的更新@Prop sCar: string = ''changeCar = (newCar: string) => {}build() {Column() {Text(`子组件 ${this.sCar}`)Button('换车').onClick((event: ClickEvent) => {// 1. prop传值 → 单向传递// 子组件, 可以修改到 prop 传值, 但是修改的更新不会同步到父组件// 通常不太会直接修改 prop 传值, 父组件的状态一旦变化, 会自动向下同步// 修改就被覆盖了// this.sCar = '小黄车'// 2. 如果实在想更新, 希望保证父子同步 => 调用父组件传递过来的方法// 如果没有写箭头函数, 意味着, this 指向 调用者, 而此处执行环境 this → 子组件this.changeCar('蹦蹦车')})}.padding(20).backgroundColor(Color.Orange)}
}@Entry
@Component
struct Index {onPageShow(): void {window.getLastWindow(AppStorage.get("context"), (err, data) => {if (err.code) {console.error('Failed to get last window. Cause:' + JSON.stringify(err));return;}data.setFullScreen(true)});}@State fCar:string = '劳斯莱斯'build() {Column() {Text(`父组件 - ${this.fCar}`)Button('换车').onClick(() => {this.fCar = '三轮车'})SonCom({sCar: this.fCar,// 这里必须要用箭头函数, 否则会有 this 指向的问题// 使用箭头函数的好处, 可以使用外部环境的 this, 不受传递过去后的执行环境影响// 希望此处 this 指向 父组件changeCar: (newCar: string) => {this.fCar = newCar}})}.padding(50).backgroundColor(Color.Pink)}
}

练习

import window from '@ohos.window';@Component
struct NumberCount {@Prop num: number = 0SubFn = () => {}AddFn = () => {}build() {Row({space: 5}) {Button('-').onClick( () => {this.SubFn()})Text(this.num.toString())Button('+').onClick( () => {this.AddFn()})}}
}@Entry
@Component
struct Index {onPageShow(): void {window.getLastWindow(AppStorage.get("context"), (err, data) => {if (err.code) {console.error('Failed to get last window. Cause:' + JSON.stringify(err));return;}data.setFullScreen(true)});}@State num1: number = 5@State num2: number = 3build() {Column({space: 5}){Row({space: 5}){Text('茄子')NumberCount({num: this.num1,AddFn: () => this.num1++,SubFn: () => this.num1--})}Row({space: 5}){Text('香蕉')NumberCount({num: this.num2,AddFn: () => this.num2++,SubFn: () => this.num2--})}}.padding(10)}
}

4.4 掘金评论案例

4.4.1 头部组件

4.4.1.1 在ets/components下新建InfoTop.ets
@Extend(Button)
function fancyButton (isOn: boolean) {.width(46).height(32).fontSize(12).padding({ left: 5, right: 5 }).backgroundColor(isOn ? '#fff' : '#F7F8FA').fontColor(isOn ? '#2f2e33' : '#8e9298').border({ width: 1, color: isOn ? '#e4e5e6' : '#F7F8FA' })
}@Component
struct InfoTop {@State isOn: boolean = truebuild() {Row(){Text('全部评论').fontColor('#222').fontSize(20).fontWeight(FontWeight.Bold)Row(){Button('最新',{ stateEffect: false}).fancyButton(this.isOn).onClick( () => this.isOn = true)Button('最热',{ stateEffect: false}).fancyButton(!this.isOn).onClick( () => this.isOn = false)}.backgroundColor('F7F8FA').borderRadius(20)}.justifyContent(FlexAlign.SpaceBetween).width('100%').height(60).padding(16)}
}export default InfoTop
4.4.1.2 Index.ets
import window from '@ohos.window';
import InfoTop from '../components/InfoTop'@Entry
@Component
struct Index {onPageShow(): void {window.getLastWindow(AppStorage.get("context"), (err, data) => {if (err.code) {console.error('Failed to get last window. Cause:' + JSON.stringify(err));return;}data.setFullScreen(true)});}build() {Column(){//   头部InfoTop()//   中间Column(){}.width('100%').backgroundColor(Color.Orange).layoutWeight(1)//   底部Row(){}.width('100%').height(60).backgroundColor(Color.Pink)}.width('100%').height('100%')}
}

4.4.2 评论案例 - List列表组件的使用

4.4.2.1 List列表基本使用

import window from '@ohos.window';
import InfoTop from '../components/InfoTop'@Entry
@Component
struct Index {onPageShow(): void {window.getLastWindow(AppStorage.get("context"), (err, data) => {if (err.code) {console.error('Failed to get last window. Cause:' + JSON.stringify(err));return;}data.setFullScreen(true)});}build() {Column(){//   头部InfoTop()//   中间List(){ForEach(Array.from({ length: 20}), () => {ListItem(){Row().width('100%').height(100).backgroundColor(Color.Brown)}.padding(10)})}.width('100%').backgroundColor(Color.Orange).layoutWeight(1).listDirection(Axis.Vertical) // 调整主轴方向 默认 Vertical  .listDirection(Axis.Horizontal).lanes(2,5) //调整列和间距.alignListItem(ListItemAlign.Center) //控制列的对齐方式.scrollBar(BarState.Auto) //Auto 按需自动显示滚动条// .divider({//   strokeWidth: 3,  //线宽//   color:  Color.Blue, //颜色//   startMargin: 10,  //左边线距离边缘的间隙//   endMargin: 10   //右边线距离边缘的间隙// })//   底部Row(){}.width('100%').height(60).backgroundColor(Color.Pink)}.width('100%').height('100%')}
}

 4.4.2.2 中间评论列表
4.4.2.2.1 在components下新增InfoItem.ets

@Component
struct InfoItem {build() {Column() {// 1. 头像, 昵称, 等级Row() {Image($r('app.media.startIcon')).width(30).aspectRatio(1).margin({ top: 10 }).borderRadius(15)Text('黑暗王国').fontSize(13).fontColor(Color.Gray).margin({ top: 10, left: 8 })Image($r('app.media.level_6')).width(20).aspectRatio(1).margin({ top: 10, left: 8 }).borderRadius(15)}// 2. 评论内容Text('安抚巾阿范德萨见附件').fontSize(13).fontColor(Color.Black).margin({ left: 40, top: 0, bottom: 8 })// 3. 评论日期 和 点赞互动Row() {Text('3天前').fontSize(11).fontColor(Color.Gray)Row() {Image($r('app.media.like_select')).width(15).aspectRatio(1)Text('99').fontSize(11).fontColor(Color.Gray)}}.padding({ left: 40, top: 5 }).width('100%').justifyContent(FlexAlign.SpaceBetween)}.padding({ left: 15, right: 15 }).alignItems(HorizontalAlign.Start)}
}export default InfoItem
4.4.2.2.2 Index .ets
import window from '@ohos.window';
import InfoTop from '../components/InfoTop'
import InfoItem from '../components/InfoItem'@Entry
@Component
struct Index {onPageShow(): void {window.getLastWindow(AppStorage.get("context"), (err, data) => {if (err.code) {console.error('Failed to get last window. Cause:' + JSON.stringify(err));return;}data.setFullScreen(true)});}build() {Column(){//   头部InfoTop()//   中间List(){ForEach(Array.from({ length: 20}), () => {ListItem(){// 列表项组件InfoItem()}})}.layoutWeight(1).padding({bottom: 10})//   底部Row(){}.width('100%').height(60).backgroundColor(Color.Pink)}.width('100%').height('100%')}
}

4.4.3 底部评价

4.4.3.1 IconFont的使用

4.4.3.1.1 ets\fonts下存放下载的文件

略 

 iconfont-阿里巴巴矢量图标库

4.4.3.1.2 注册字体&使用字体
import window from '@ohos.window';
import InfoTop from '../components/InfoTop'
import InfoItem from '../components/InfoItem'
import font from '@ohos.font';@Entry
@Component
struct Index {// 一加载Index入口页面, 就进行注册// aboutToAppear → 会在组件一加载时, 自动调用执行(生命周期函数)aboutToAppear(): void {// 1. 注册字体font.registerFont({familyName: 'myfont',familySrc: '/fonts/iconfont.ttf'})}onPageShow(): void {window.getLastWindow(AppStorage.get("context"), (err, data) => {if (err.code) {console.error('Failed to get last window. Cause:' + JSON.stringify(err));return;}data.setFullScreen(true)});}build() {Column(){// 2. 使用字体测试Text('\ue600').fontFamily('myfont').fontSize(30).fontColor(Color.Red)Text('\ue840').fontFamily('myfont').fontSize(30).fontWeight(FontWeight.Bold).fontColor(Color.Red)//   头部InfoTop()//   中间List(){ForEach(Array.from({ length: 20}), () => {ListItem(){// 列表项组件InfoItem()}})}.layoutWeight(1).padding({bottom: 10})//   底部Row(){}.width('100%').height(60).backgroundColor(Color.Pink)}.width('100%').height('100%')}
}

4.4.3.2  在components下新增InfoBottom.ets
@Component
struct InfoBottom {build() {Row() {Row() {Text('\ue840').fontFamily('myfont').fontSize(18).margin({ left: 20 })TextInput({placeholder: '写评论...'}).backgroundColor(Color.Transparent)}.height(40).backgroundColor('#f5f6f5').borderRadius(20).margin({ left: 15, right: 20, top: 10, bottom: 10 }).layoutWeight(1)Text('\ue600').fontFamily('myfont').fontSize(26).margin({ left: 6, right: 6 })Text('\ue61d').fontFamily('myfont').fontSize(26).margin({ left: 6, right: 6 })}.height(60).width('100%')}
}export default InfoBottom
4.4.3.3  Index .ets
import window from '@ohos.window';
import InfoTop from '../components/InfoTop'
import InfoItem from '../components/InfoItem'
import font from '@ohos.font';
import InfoBottom from '../components/InfoBottom';@Entry
@Component
struct Index {// 一加载Index入口页面, 就进行注册// aboutToAppear → 会在组件一加载时, 自动调用执行(生命周期函数)aboutToAppear(): void {// 1. 注册字体font.registerFont({familyName: 'myfont',familySrc: '/fonts/iconfont.ttf'})}onPageShow(): void {window.getLastWindow(AppStorage.get("context"), (err, data) => {if (err.code) {console.error('Failed to get last window. Cause:' + JSON.stringify(err));return;}data.setFullScreen(true)});}build() {Column(){// 2. 使用字体测试// Text('\ue600')//   .fontFamily('myfont')//   .fontSize(30)//   .fontColor(Color.Red)//// Text('\ue840')//   .fontFamily('myfont')//   .fontSize(30)//   .fontWeight(FontWeight.Bold)//   .fontColor(Color.Red)//   头部InfoTop()//   中间List(){ForEach(Array.from({ length: 20}), () => {ListItem(){// 列表项组件InfoItem()}})}.layoutWeight(1).padding({bottom: 10})//   底部InfoBottom()}.width('100%').height('100%')}
}

4.4.4 列表渲染

4.4.4.1 准备数据

ets\model\CommentData.ets

// 准备评论的数据类
export class CommentData {avatar: string; // 头像name: string; // 昵称level: number; //用户等级likeNum: number; //点赞数量commentTxt: string; //评论内容isLike: boolean; //是否喜欢levelIcon: Resource // level等级timeString: string // 发布时间-基于时间戳处理后,展示给用户看的属性(2天前)time: number // 时间戳-数字格式的日期constructor(avatar: string, name: string, time: number, level: number, lickNum: number, commentTxt: string, isLike: boolean, ) {this.avatar = avatarthis.name = namethis.timeString = this.convertTime(time) // 拿到的时间格式, 通常是时间戳的格式  1645820201123this.time = timethis.level = levelthis.likeNum = lickNumthis.commentTxt = commentTxtthis.isLike = isLikethis.levelIcon = this.convertLevel(this.level) // 基于等级数字, 转换成图片路径}convertTime(timestamp: number) {// 获取当前的时间戳const currentTimestamp = new Date().getTime();const timeDifference = (currentTimestamp - timestamp) / 1000; // 转换为秒if (timeDifference < 5 || timeDifference == 0) {return "刚刚";} else if (timeDifference < 60) {return `${Math.floor(timeDifference)}秒前`;} else if (timeDifference < 3600) {return `${Math.floor(timeDifference / 60)}分钟前`;} else if (timeDifference < 86400) {return `${Math.floor(timeDifference / 3600)}小时前`;} else if (timeDifference < 604800) {return `${Math.floor(timeDifference / 86400)}天前`;} else if (timeDifference < 2592000) {return `${Math.floor(timeDifference / 604800)}周前`;} else if (timeDifference < 31536000) {return `${Math.floor(timeDifference / 2592000)}个月前`;} else {return `${Math.floor(timeDifference / 31536000)}年前`;}}// 基于传入的level,转换成图片路径convertLevel(level: number) {const iconList = [$r('app.media.level_1'),$r('app.media.level_2'),$r('app.media.level_3'),$r('app.media.level_4'),$r('app.media.level_5'),$r('app.media.level_6'),]return iconList[level]}
}// 封装一个方法, 创建假数据
export const createListRange = (): CommentData[] => {let result: CommentData[] = new Array()result = [new CommentData(`https://fastly.picsum.photos/id/770/600/600.jpg?hmac=tuK9EHg1ifTU3xKAiZj2nHSdWy4mk7enhylgOc2BW7E`, "雪山飞狐", 1645820201123, Math.floor(Math.random()*6) , Math.floor(Math.random()*100), '23年一年干完的事😂😂😂真的非常仓促', false),new CommentData(`https://fastly.picsum.photos/id/225/600/600.jpg?hmac=v97zt_t4mxeyMttX_m09pxhCvftiTxFR1MMBZi5HQxs`, "千纸鹤", 1677356201123, Math.floor(Math.random()*6) , Math.floor(Math.random()*100), 'Netty对象池源码分析来啦!juejin.cn欢迎点赞[奸笑]', false),new CommentData(`https://fastly.picsum.photos/id/122/600/600.jpg?hmac=1oA93YbjYVt96DcJcGQ5PLthzjUsdtrnBQaM0USBozI`, "烟雨江南", 1688772201123, Math.floor(Math.random()*6) , Math.floor(Math.random()*100), '有一个不听劝的Stable Diffusion出图的小伙伴,找我给她装填脑。 一个资深的IT工程师不能受这个委屈。', false),new CommentData(`https://fastly.picsum.photos/id/654/600/600.jpg?hmac=ewnK6Bx_MKQLJa9waZOV1xNO7--K5oSwCShtz1JDYw8`, "魔法小精灵", 1697484201123, Math.floor(Math.random()*6) , Math.floor(Math.random()*100), '有一点可以确信 前后端开发界限越来越模糊。后端可以不写 但是不能不会。', false),new CommentData(`https://fastly.picsum.photos/id/345/600/600.jpg?hmac=EQflzbIadAglm0RzotyKXM2itPfC49fR3QE7eW_UaPo`, "独行侠", 1704067200000, Math.floor(Math.random()*6) , Math.floor(Math.random()*100), '今天看到一个帖子,挺有意思的。', false),new CommentData(`https://fastly.picsum.photos/id/905/600/600.jpg?hmac=DvIKicBZ45DEZoZFwdZ62VbmaCwkK4Sv7rwYzUvwweU`, "枫叶飘零", 1706745600000, Math.floor(Math.random()*6) , Math.floor(Math.random()*100), '我想了搞钱的路子, 1:投资理财, 后来发下,不投资就是最好的理财, 2:买彩票,后来发现彩票都是人家预定好的,根本不是随机的,卒, 3:开店创业,隔行如隔山,开店失败,卒。', false),new CommentData(`https://fastly.picsum.photos/id/255/600/600.jpg?hmac=-lfdnAl71_eAIy1OPAupFFPh7EOJPmQRJFg-y7lRB3s`, "星空漫步", 1707523200000, Math.floor(Math.random()*6) , Math.floor(Math.random()*100), '优胜劣汰,自然选择吧,没什么好怪的。都是crud,招个大学生就能干了。', false),new CommentData(`https://fastly.picsum.photos/id/22/600/600.jpg?hmac=QEZq7KUHwBZCt3kGSEHMwJlZfnzCxCeBgHjYj7iQ-UY`, "剑指苍穹", 1708300800000, Math.floor(Math.random()*6) , Math.floor(Math.random()*100), '白嫖ChatGPT4的功能。然后,抱着试一试的态度把玩了一下。发现真的好用。', false),new CommentData(`https://fastly.picsum.photos/id/439/600/600.jpg?hmac=LC9k_bzrN0NhKRyV62fou3ix3cRFZKNfAyXgxGs6zh8`, "黑暗王国", 1708646400000, Math.floor(Math.random()*6) , Math.floor(Math.random()*100), '字数越少,事情越大。', false),]return result
}
4.4.4.2 列表渲染完成

 InfoItem.etc

import window from '@ohos.window';
import InfoTop from '../components/InfoTop'
import InfoItem from '../components/InfoItem'
import font from '@ohos.font';
import InfoBottom from '../components/InfoBottom';
import { CommentData, createListRange } from '../model/CommentData'import { CommentData } from '../model/CommentData'
@Component
struct InfoItem {@Prop itemObj: CommentData@Prop index: numberonLikeClick = (index: number) => {}build() {Column() {// 1. 头像, 昵称, 等级Row() {Image(this.itemObj.avatar).width(30).aspectRatio(1).margin({ top: 10 }).borderRadius(15)Text(this.itemObj.name).fontSize(13).fontColor(Color.Gray).margin({ top: 10, left: 8 })Image(this.itemObj.levelIcon).width(20).aspectRatio(1).margin({ top: 10, left: 8 }).borderRadius(15)}// 2. 评论内容Text(this.itemObj.commentTxt).fontSize(13).fontColor(Color.Black).margin({ left: 40, top: 0, bottom: 8 })// 3. 评论日期 和 点赞互动Row() {Text(this.itemObj.timeString).fontSize(11).fontColor(Color.Gray)Row() {Image($r('app.media.like_unselect')).width(15).aspectRatio(1)Text(this.itemObj.likeNum.toString()).fontSize(11).fontColor(Color.Gray)}}.padding({ left: 40, top: 5 }).width('100%').justifyContent(FlexAlign.SpaceBetween)}.padding({ left: 15, right: 15 }).alignItems(HorizontalAlign.Start)}
}export default InfoItem
// 创建数据测试
// let listArr: CommentData[] = createListRange()
// console.log('数组列表', JSON.stringify(listArr))// 从1970年01月01号 0:0:0
// 时间戳 → 特殊的日期格式(数字类型), 利于比较运算
// let nowTime: number = new Date().getTime() // 05-16
// let lastTime: number = new Date('2024-03-02').getTime()
// console.log('时间戳', nowTime, lastTime)
// console.log('时间戳', (nowTime - lastTime) / 1000 / 3600 / 24)
@Entry
@Component
struct Index {@State commentList: CommentData[] = createListRange()// 一加载Index入口页面, 就进行注册// aboutToAppear → 会在组件一加载时, 自动调用执行(生命周期函数)aboutToAppear(): void {// 1. 注册字体font.registerFont({familyName: 'myfont',familySrc: '/fonts/iconfont.ttf'})}onPageShow(): void {window.getLastWindow(AppStorage.get("context"), (err, data) => {if (err.code) {console.error('Failed to get last window. Cause:' + JSON.stringify(err));return;}data.setFullScreen(true)});}build() {Column(){// 2. 使用字体测试// Text('\ue600')//   .fontFamily('myfont')//   .fontSize(30)//   .fontColor(Color.Red)//// Text('\ue840')//   .fontFamily('myfont')//   .fontSize(30)//   .fontWeight(FontWeight.Bold)//   .fontColor(Color.Red)//   头部InfoTop()//   中间List(){ForEach(this.commentList, (item: CommentData, index: number) => {ListItem(){// 列表项组件 -- 将item对象, 通过prop传值, 传递下去InfoItem({itemObj: item})}})}.layoutWeight(1).padding({bottom: 10})//   底部InfoBottom()}.width('100%').height('100%')}
}

Index.ets

import window from '@ohos.window';
import InfoTop from '../components/InfoTop'
import InfoItem from '../components/InfoItem'
import font from '@ohos.font';
import InfoBottom from '../components/InfoBottom';
import { CommentData, createListRange } from '../model/CommentData'// 创建数据测试
// let listArr: CommentData[] = createListRange()
// console.log('数组列表', JSON.stringify(listArr))// 从1970年01月01号 0:0:0
// 时间戳 → 特殊的日期格式(数字类型), 利于比较运算
// let nowTime: number = new Date().getTime() // 05-16
// let lastTime: number = new Date('2024-03-02').getTime()
// console.log('时间戳', nowTime, lastTime)
// console.log('时间戳', (nowTime - lastTime) / 1000 / 3600 / 24)
@Entry
@Component
struct Index {@State commentList: CommentData[] = createListRange()// 一加载Index入口页面, 就进行注册// aboutToAppear → 会在组件一加载时, 自动调用执行(生命周期函数)aboutToAppear(): void {// 1. 注册字体font.registerFont({familyName: 'myfont',familySrc: '/fonts/iconfont.ttf'})}onPageShow(): void {window.getLastWindow(AppStorage.get("context"), (err, data) => {if (err.code) {console.error('Failed to get last window. Cause:' + JSON.stringify(err));return;}data.setFullScreen(true)});}build() {Column(){// 2. 使用字体测试// Text('\ue600')//   .fontFamily('myfont')//   .fontSize(30)//   .fontColor(Color.Red)//// Text('\ue840')//   .fontFamily('myfont')//   .fontSize(30)//   .fontWeight(FontWeight.Bold)//   .fontColor(Color.Red)//   头部InfoTop()//   中间List(){ForEach(this.commentList, (item: CommentData, index: number) => {ListItem(){// 列表项组件 -- 将item对象, 通过prop传值, 传递下去InfoItem({itemObj: item})}})}.layoutWeight(1).padding({bottom: 10})//   底部InfoBottom()}.width('100%').height('100%')}
}

4.4.4.3 点赞

infoItem.ets

import { CommentData } from '../model/CommentData'
@Component
struct InfoItem {@Prop itemObj: CommentData@Prop index: numberonLikeClick = (index: number) => {}build() {Column() {// 1. 头像, 昵称, 等级Row() {Image(this.itemObj.avatar).width(30).aspectRatio(1).margin({ top: 10 }).borderRadius(15)Text(this.itemObj.name).fontSize(13).fontColor(Color.Gray).margin({ top: 10, left: 8 })Image(this.itemObj.levelIcon).width(20).aspectRatio(1).margin({ top: 10, left: 8 }).borderRadius(15)}// 2. 评论内容Text(this.itemObj.commentTxt).fontSize(13).fontColor(Color.Black).margin({ left: 40, top: 0, bottom: 8 })// 3. 评论日期 和 点赞互动Row() {Text(this.itemObj.timeString).fontSize(11).fontColor(Color.Gray)Row() {Image(this.itemObj.isLike ? $r('app.media.like_select') : $r('app.media.like_unselect')).width(15).aspectRatio(1)Text(this.itemObj.likeNum.toString()).fontSize(11).fontColor(this.itemObj.isLike ? Color.Blue : Color.Gray)}.onClick(() => {// 此处的this是子组件// 让当前项的 likeNum 变化 (老爹的数据, 需要调用老爹的方法才能改)this.onLikeClick(this.index)})}.padding({ left: 40, top: 5 }).width('100%').justifyContent(FlexAlign.SpaceBetween)}.padding({ left: 15, right: 15 }).alignItems(HorizontalAlign.Start)}
}export default InfoItem

Index.ets

import window from '@ohos.window';
import InfoTop from '../components/InfoTop'
import InfoItem from '../components/InfoItem'
import font from '@ohos.font';
import InfoBottom from '../components/InfoBottom';
import { CommentData, createListRange } from '../model/CommentData'// 创建数据测试
// let listArr: CommentData[] = createListRange()
// console.log('数组列表', JSON.stringify(listArr))// 从1970年01月01号 0:0:0
// 时间戳 → 特殊的日期格式(数字类型), 利于比较运算
// let nowTime: number = new Date().getTime() // 05-16
// let lastTime: number = new Date('2024-03-02').getTime()
// console.log('时间戳', nowTime, lastTime)
// console.log('时间戳', (nowTime - lastTime) / 1000 / 3600 / 24)
@Entry
@Component
struct Index {@State commentList: CommentData[] = createListRange()// 一加载Index入口页面, 就进行注册// aboutToAppear → 会在组件一加载时, 自动调用执行(生命周期函数)aboutToAppear(): void {// 1. 注册字体font.registerFont({familyName: 'myfont',familySrc: '/fonts/iconfont.ttf'})}// 处理喜欢handleLike (index: number) {// 1. 获取下标 或 id 加以区分// 子调用父的方法时, 每个子都能调用父的方法, 需要加以区分// 此时由于是数组操作, 可以传递下标, [快速定位到操作的是哪项]// 将来发请求 → id 作为区分值, 也可以// AlertDialog.show({//   message: index.toString()// })// 2. 父组件的方法, 如果抽取出来, 如果直接传递给子组件//    会有 this 指向问题, this 通常直接指向调用者//    需要, 用箭头函数函数包一层, 保证 this 还是指向父组件//    console.log('父组件的数据', this.commentList)// 3. 根据 index, 根据 数组数据, 进行判断 +1 还是 -1let itemData = this.commentList[index]if (itemData.isLike) {itemData.likeNum -= 1}else {itemData.likeNum += 1}itemData.isLike = !itemData.isLike// 4. 对于复杂类型: 状态对象, 状态数组, 只会对第一层数据, 进行监视变化// 数组.splice(从哪开始删除, 删除几个, 替换的项1, 替换的项2, ...)this.commentList.splice(index, 1, itemData)}onPageShow(): void {window.getLastWindow(AppStorage.get("context"), (err, data) => {if (err.code) {console.error('Failed to get last window. Cause:' + JSON.stringify(err));return;}data.setFullScreen(true)});}build() {Column(){// 2. 使用字体测试// Text('\ue600')//   .fontFamily('myfont')//   .fontSize(30)//   .fontColor(Color.Red)//// Text('\ue840')//   .fontFamily('myfont')//   .fontSize(30)//   .fontWeight(FontWeight.Bold)//   .fontColor(Color.Red)//   头部InfoTop()// 中间List() {ForEach(this.commentList, (item: CommentData, index: number) => {ListItem() {// 列表项组件 - 将item对象, 通过prop传值, 传递下去InfoItem({index: index,itemObj: item,onLikeClick: (index: number) => {// 此处的this → 访问的是外部环境的 this → 就是父组件this.handleLike(index)}})}})}.layoutWeight(1).padding({ bottom: 10 })// 底部InfoBottom()}.width('100%').height('100%')}
}

4.4.4.4 添加评论

infoBottom.ets

@Component
struct InfoBottom {@State txt: string = ''onSubmitComment = (content: string) => {}build() {Row() {Row() {Text('\ue840').fontFamily('myfont').fontSize(18).margin({ left: 20 })TextInput({placeholder: '写评论...',text: $$this.txt}).backgroundColor(Color.Transparent).fontSize(18).onSubmit(() => {// 这里不能直接添加, 需要调用父组件传递过来的方法this.onSubmitComment(this.txt)})}.height(40).backgroundColor('#f5f6f5').borderRadius(20).margin({ left: 15, right: 20, top: 10, bottom: 10 }).layoutWeight(1)Text('\ue600').fontFamily('myfont').fontSize(26).margin({ left: 6, right: 6 })Text('\ue61d').fontFamily('myfont').fontSize(26).margin({ left: 6, right: 6 })}.height(60).width('100%')}
}export default InfoBottom

Index.ets

import window from '@ohos.window';
import InfoTop from '../components/InfoTop'
import InfoItem from '../components/InfoItem'
import font from '@ohos.font';
import InfoBottom from '../components/InfoBottom';
import { CommentData, createListRange } from '../model/CommentData'// 创建数据测试
// let listArr: CommentData[] = createListRange()
// console.log('数组列表', JSON.stringify(listArr))// 从1970年01月01号 0:0:0
// 时间戳 → 特殊的日期格式(数字类型), 利于比较运算
// let nowTime: number = new Date().getTime() // 05-16
// let lastTime: number = new Date('2024-03-02').getTime()
// console.log('时间戳', nowTime, lastTime)
// console.log('时间戳', (nowTime - lastTime) / 1000 / 3600 / 24)
@Entry
@Component
struct Index {@State commentList: CommentData[] = createListRange()// 一加载Index入口页面, 就进行注册// aboutToAppear → 会在组件一加载时, 自动调用执行(生命周期函数)aboutToAppear(): void {// 1. 注册字体font.registerFont({familyName: 'myfont',familySrc: '/fonts/iconfont.ttf'})}// 处理喜欢handleLike (index: number) {// 1. 获取下标 或 id 加以区分// 子调用父的方法时, 每个子都能调用父的方法, 需要加以区分// 此时由于是数组操作, 可以传递下标, [快速定位到操作的是哪项]// 将来发请求 → id 作为区分值, 也可以// AlertDialog.show({//   message: index.toString()// })// 2. 父组件的方法, 如果抽取出来, 如果直接传递给子组件//    会有 this 指向问题, this 通常直接指向调用者//    需要, 用箭头函数函数包一层, 保证 this 还是指向父组件//    console.log('父组件的数据', this.commentList)// 3. 根据 index, 根据 数组数据, 进行判断 +1 还是 -1let itemData = this.commentList[index]if (itemData.isLike) {itemData.likeNum -= 1}else {itemData.likeNum += 1}itemData.isLike = !itemData.isLike// 4. 对于复杂类型: 状态对象, 状态数组, 只会对第一层数据, 进行监视变化// 数组.splice(从哪开始删除, 删除几个, 替换的项1, 替换的项2, ...)this.commentList.splice(index, 1, itemData)}// 处理提交handleSubmit (content: string) {// 往数组的最前面, 新增一项const newItem: CommentData = new CommentData("https://img0.baidu.com/it/u=3316636492,2799302396&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1708707600&t=fc2a4907d0fae5c7b2d2d5f1511c24b3","我", new Date().getTime(), 5, 0, content, false)this.commentList = [newItem, ...this.commentList]}onPageShow(): void {window.getLastWindow(AppStorage.get("context"), (err, data) => {if (err.code) {console.error('Failed to get last window. Cause:' + JSON.stringify(err));return;}data.setFullScreen(true)});}build() {Column(){// 2. 使用字体测试// Text('\ue600')//   .fontFamily('myfont')//   .fontSize(30)//   .fontColor(Color.Red)//// Text('\ue840')//   .fontFamily('myfont')//   .fontSize(30)//   .fontWeight(FontWeight.Bold)//   .fontColor(Color.Red)//   头部InfoTop()// 中间List() {ForEach(this.commentList, (item: CommentData, index: number) => {ListItem() {// 列表项组件 - 将item对象, 通过prop传值, 传递下去InfoItem({index: index,itemObj: item,onLikeClick: (index: number) => {// 此处的this → 访问的是外部环境的 this → 就是父组件this.handleLike(index)}})}})}.layoutWeight(1).padding({ bottom: 10 })// 底部InfoBottom({onSubmitComment: (content: string) => {this.handleSubmit(content)}})}.width('100%').height('100%')}
}

 

4.4.4.5 评论排序

 infoTop.ets

@Extend(Button)
function fancyButton (isOn: boolean) {.width(46).height(32).fontSize(12).padding({ left: 5, right: 5 }).backgroundColor(isOn ? '#fff' : '#F7F8FA').fontColor(isOn ? '#2f2e33' : '#8e9298').border({ width: 1, color: isOn ? '#e4e5e6' : '#F7F8FA' })
}@Component
struct InfoTop {@State isOn: boolean = trueonSort = (type: number) => {}build() {Row() {Text('全部评论').fontColor('#222').fontSize(20).fontWeight(FontWeight.Bold)Row() {Button('最新', { stateEffect: false }).fancyButton(this.isOn).onClick(() => {this.isOn = truethis.onSort(0)})Button('最热', { stateEffect: false }).fancyButton(!this.isOn).onClick(() => {this.isOn = falsethis.onSort(1)})}.backgroundColor('#F7F8FA').borderRadius(20)}.width('100%').justifyContent(FlexAlign.SpaceBetween).padding(16)}
}export default InfoTop

Index.ets

import InfoTop from '../components/InfoTop'
import InfoItem from '../components/InfoItem'
import InfoBottom from '../components/InfoBottom'
import font from '@ohos.font'
import { CommentData, createListRange } from '../model/CommentData'// 创建数据测试
// let listArr: CommentData[] = createListRange()
// console.log('数组列表', JSON.stringify(listArr))// 从1970年01月01号 0:0:0
// 时间戳 → 特殊的日期格式(数字类型), 利于比较运算
// let nowTime: number = new Date().getTime() // 05-16
// let lastTime: number = new Date('2024-03-02').getTime()
// console.log('时间戳', nowTime, lastTime)
// console.log('时间戳', (nowTime - lastTime) / 1000 / 3600 / 24)@Entry
@Component
struct Index {@State commentList: CommentData[] = createListRange()// 一加载Index入口页面, 就进行注册// aboutToAppear → 会在组件一加载时, 自动调用执行(生命周期函数)aboutToAppear(): void {// 1. 注册字体font.registerFont({familyName: 'myfont',familySrc: '/fonts/iconfont.ttf'})this.handleSort(0)}// 处理喜欢handleLike (index: number) {// 1. 获取下标 或 id 加以区分// 子调用父的方法时, 每个子都能调用父的方法, 需要加以区分// 此时由于是数组操作, 可以传递下标, [快速定位到操作的是哪项]// 将来发请求 → id 作为区分值, 也可以// AlertDialog.show({//   message: index.toString()// })// 2. 父组件的方法, 如果抽取出来, 如果直接传递给子组件//    会有 this 指向问题, this 通常直接指向调用者//    需要, 用箭头函数函数包一层, 保证 this 还是指向父组件//    console.log('父组件的数据', this.commentList)// 3. 根据 index, 根据 数组数据, 进行判断 +1 还是 -1let itemData = this.commentList[index]if (itemData.isLike) {itemData.likeNum -= 1}else {itemData.likeNum += 1}itemData.isLike = !itemData.isLike// 4. 对于复杂类型: 状态对象, 状态数组, 只会对第一层数据, 进行监视变化// 数组.splice(从哪开始删除, 删除几个, 替换的项1, 替换的项2, ...)this.commentList.splice(index, 1, itemData)}// 处理提交handleSubmit (content: string) {// 往数组的最前面, 新增一项const newItem: CommentData = new CommentData("https://img0.baidu.com/it/u=3316636492,2799302396&fm=253&app=120&size=w931&n=0&f=JPEG&fmt=auto?sec=1708707600&t=fc2a4907d0fae5c7b2d2d5f1511c24b3","我", new Date().getTime(), 5, 0, content, false)this.commentList = [newItem, ...this.commentList]}// 处理排序 0 最新 time时间戳,  1 最热 likeCount 点赞数handleSort (type: number) {if (type === 0) {// 时间戳, 从大到小排序this.commentList.sort((a, b) => {// a 前一项, b 后一项return b.time - a.time // 返回值如果 > 0, 交换位置})}else {// 点赞数, 从大到小排序this.commentList.sort((a, b) => {return b.likeNum - a.likeNum})}}build() {Column() {// 2. 使用字体测试// Text('\ue600')//   .fontFamily('myfont')//   .fontSize(30)//   .fontColor(Color.Red)// 头部标题组件InfoTop({onSort: (type: number) => {this.handleSort(type)}})// 中间List() {ForEach(this.commentList, (item: CommentData, index: number) => {ListItem() {// 列表项组件 - 将item对象, 通过prop传值, 传递下去InfoItem({index: index,itemObj: item,onLikeClick: (index: number) => {// 此处的this → 访问的是外部环境的 this → 就是父组件this.handleLike(index)}})}})}.layoutWeight(1).padding({ bottom: 10 })// 底部InfoBottom({onSubmitComment: (content: string) => {this.handleSubmit(content)}})}.width('100%').height('100%')}
}

4.5 @Link双向同步 

interface Person {name: stringage: number
}@Entry
@Component// 父组件
struct KnowledgePage {@State count: number = 0@State person: Person = {name: 'zs',age: 18}build() {Column() {Text('父组件').fontSize(30)Text(this.count.toString())Text(JSON.stringify(this.person))Button('修改数据').onClick(() => {this.count++})SonComponent({count: this.count,person: this.person})}.padding(10).height('100%').backgroundColor('#eee').width('100%').alignItems(HorizontalAlign.Center).padding({ top: 100 })}
}@Component// 子组件
struct SonComponent {@Link count:number@Link person: Person// 编写 UIbuild() {Column({ space: 20 }) {Text('我是子组件').fontSize(20)Text(this.count.toString())Text(JSON.stringify(this.person))Column() {Button('修改count').onClick(() => {// this.count++this.person.age++})}}.backgroundColor('#a6c398').alignItems(HorizontalAlign.Center).width('80%').margin({ top: 100 }).padding(10).borderRadius(10)}
}

 4.6 @Provide \@Consume后代组件

interface Car {name: stringbrand: string
}@Entry
@Component// 顶级组件
struct RootComponent {@Provide themeColor: string = 'yellow'@Provide car: Car = {name: '小黄',brand: '美团'}build() {Column() {Text('顶级组件').fontSize(30).fontWeight(900)Text(this.themeColor)Text(JSON.stringify(this.car))// 二级组件ParentComponent()ParentComponent()}.padding(10).height('100%').backgroundColor('#ccc').width('100%').alignItems(HorizontalAlign.Center).padding({ top: 100 })}
}@Component// 二级组件
struct ParentComponent {@Consume themeColor: string// 编写 UIbuild() {Column({ space: 20 }) {Text('我是二级组件').fontSize(22).fontWeight(900)Text(this.themeColor)// 内层子组件SonComponent()}.backgroundColor('#a6c398').alignItems(HorizontalAlign.Center).width('90%').margin({ top: 50 }).padding(10).borderRadius(10)}
}@Component// 内层组件
struct SonComponent {@Consume themeColor: string@Consume car: Car// 编写 UIbuild() {Column({ space: 20 }) {Text('我是内层组件' + this.themeColor).fontSize(20).fontWeight(900).onClick(() => {// this.themeColor = 'orange'this.car.name = '小绿'})Text(JSON.stringify(this.car))}.backgroundColor('#bf94e4').alignItems(HorizontalAlign.Center).width('90%').margin({ top: 50 }).padding(10).borderRadius(10)}
}

4.7 @ Observed &@ObjectLink嵌套对象数组属性变化

interface IPerson {id: numbername: stringage: number
}@Observed
class Person {id: numbername: stringage: numberconstructor(obj: IPerson) {this.id = obj.idthis.name = obj.namethis.age = obj.age}
}@Entry
@Component
struct ObservedAndLink {@State personList: Person[] = [new Person({id: 1,name: '张三',age: 18}),new Person({id: 2,name: '李四',age: 19}),new Person({id: 3,name: '王五',age: 20})]build() {Column({ space: 20 }) {Text('父组件').fontSize(30)List({ space: 10 }) {ForEach(this.personList, (item: Person, index: number) => {ItemCom({info: item,addAge: () => {// 修改嵌套的数据 => 普通的情况, 监视不到更新item.age++ // 如果能监视到AlertDialog.show({message: JSON.stringify(this.personList)})// this.personList.splice(index, 1, item) // 无需手动替换更新}})})}}.backgroundColor('#cbe69b').width('100%').height('100%').padding(20)}
}@Component
struct ItemCom {@ObjectLink info: PersonaddAge = () => {}build() {ListItem() {Row({ space: 10 }) {Text('姓名:' + this.info.name)Text('年龄:' + this.info.age)Blank()Button('修改数据').onClick(() => {// this.addAge()this.info.age++})}.backgroundColor(Color.Pink).padding(10).width('100%')}}
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/54343.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大数据Flink(一百二十二):阿里云Flink MySQL连接器介绍

文章目录 阿里云Flink MySQL连接器介绍 一、特色功能 二、​​​​​​​语法结构 三、​​​​​​​​​​​​​​WITH参数 阿里云Flink MySQL连接器介绍 阿里云提供了MySQL连接器&#xff0c;其作为源表时&#xff0c;扮演的就是flink cdc的角色。 一、特色功能 MySQ…

neo4j导入csv数据

neo4j数据可视化实践 手动输入数据 - 官方democsv数据导入准备数据数据处理导入步骤① 导入疾病表格② 导入药物表格③导入疾病-药物关系表格 爬虫的csv文件 手动输入数据 - 官方demo 点击之后&#xff0c;按照左边10张图中的代码&#xff0c;复制粘贴熟悉语法 效果如下 csv数据…

基于JAVA+SpringBoot+Vue的学生干部管理系统

基于JAVASpringBootVue的学生干部管理系统 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末附源码下载链接&#x1f345; 哈…

【AI视频】Runway:Gen-2 运镜详解

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AI视频 | Runway 文章目录 &#x1f4af;前言&#x1f4af;Camera Control&#xff08;运镜&#xff09;&#x1f4af;Camera Control功能测试Horizonta&#xff08;左右平移&#xff09;Vertical&#xff08;上下平移&#xff0…

UNION嵌套STRUCT的两种类型

1. STRUCT里面的总长度大于UNION中的最大长度 在UNION类型中&#xff0c;嵌套如STRUCT类型&#xff0c;其中STRUCT的类型还比UNION类型中最大的类型的长度还长的时候&#xff0c;会如何处理呢&#xff0c;看下面示例 程序源码 #include "stdafx.h"typedef unsigned…

103.WEB渗透测试-信息收集-FOFA语法(3)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;102.WEB渗透测试-信息收集-FOFA语法&#xff08;2&#xff09; FOFA使用实例 组件框架 …

DAY20信息打点-红蓝队自动化项目资产侦察武器库部署企查产权网络空间

2.自动化-网络空间-AsamF 1.去GitHub上下载项目之后使用CMD打开 2.输入命令AsamF_windows_amd64.exe -v生成配置文件 3.AsamF会在~/.config/asamf/目录下生成config.json文件 C:\Users\Acer\.config\asamf 5.根据文档输入命令去查询所需信息&#xff08;已经没有用了&#x…

LabVIEW多语言支持优化

遇到的LabVIEW多语言支持问题&#xff0c;特别是德文显示乱码以及系统区域设置导致的异常&#xff0c;可能是由编码问题或区域设置不匹配引起的。以下是一些可能的原因及解决方案&#xff1a; 问题原因&#xff1a; 编码问题&#xff1a;LabVIEW内部使用UTF-8编码&#xff0c;但…

react的事件绑定

文章目录 基本示例使用箭头函数事件对象阻止默认行为绑定事件处理函数的上下文 在 React 中&#xff0c;事件绑定主要通过 JSX 属性来实现。事件处理函数被传递给相应的事件属性&#xff0c;例如 onClick、onChange 等。这些属性会被绑定到 HTML 元素上&#xff0c;并在事件发生…

大白话解读末日期权是什么意思?末日期权与黑天鹅!

今天带你了解大白话解读末日期权是什么意思&#xff1f;末日期权与黑天鹅&#xff01;末日期权与黑天鹅事件的关系主要体现在风险和波动性管理上&#xff0c;交易者需要谨慎对待这两者的互动。 末日期权和期权黑天鹅事件之间的关系主要体现在风险管理和市场波动性上。 末日期…

【SA8155P】AIS Camera相关内容的简单介绍

高通车载相机模块(AIS,Automotive lmage System)是专门针对车载系统特性而设计的一套车载视觉架构,可用于AVM、RVC、DMS等常见车载视频应用开发。车载Camera系统的图像大部分是给自动驾驶等使用,更多考虑的是远距离传输、多摄像头图像处理等场景。 本文仅对AIS Camera相关…

2024考研数学真题解析-数二:

第一类间断点&#xff1a;可去间断点&#xff0c;跳跃间断点。 幂指函数x的取值范围是x>0。 接着分母不能为0推出x1&#xff0c;x2是间断点&#xff0c;由幂指函数x>0可知&#xff0c;x0也是间断点。 先求x0处的极限&#xff0c;这里没有必要求0左右两边的极限&#xff0…

百元头戴式耳机都有哪些?五大精品独家推荐!

在当今市场中&#xff0c;耳机已经成为我们生活中不可或缺的电子设备之一。而对于追求性价比的朋友来说&#xff0c;如何在百元价位内挑选到一款音质出色、舒适耐用的头戴式耳机&#xff0c;无疑是一大难题。百元头戴式耳机都有哪些&#xff1f;为了帮助大家在琳琅满目的产品中…

Linux之CentOS 7.9-Minimal部署Oracle 11g r2 安装实测验证(桌面模式)

前言: 发个之前的库存… Linux之CentOS 7.9-Minimal部署Oracle 11g r2 安装实测验证(桌面模式) 本次验证的是CentOS_7_Minimal-2009桌面模式来部署Oracle 11g r2,大家可根据自身环境及学习来了解。 环境:下载地址都给你们超链好了 1、Linux系统镜像包: 1.1 CentOS-7-x86_…

嵌入式AI---如何用C++实现YOLO的NMS(非极大值抑制)算法

文章目录 前言一、为什么需要NMS算法&#xff1f;二、什么是NMS算法&#xff1f;三、如何使用C编写一个NMS算法1、预测框定义2、滤除无效框 总结 前言 YOLO系列的目标检测算法在边缘部署方面展现出了强大的性能和广泛的应用潜力。大部分业务场景是利用PyTorch在服务器端完成检…

【网络安全】-ssrf服务器请求伪造攻击-burp

SSRF攻击服务器请求伪造攻击 CSRF攻击跨站请求伪造攻击也称客户端请求伪造攻击 两种攻击最主要的区别是一个在服务器&#xff0c;一个在客户端。 文章目录 前言 什么是SSRF攻击? 1.分类&#xff1a; 针对服务器的 SSRF 攻击&#xff1a; 针对后端系统的SSRF攻击&#xff1a; …

【Echarts】vue3打开echarts的正确方式

ECharts 是一个功能强大、灵活易用的数据可视化工具&#xff0c;适用于商业报表、数据分析、科研教育等多种场景。那么该如何优雅的使用Echarts呢? 这里以vue3为例。 安装echarts pnpm i echarts封装公用方法 // ts-nocheck import * as echarts from echarts; // 我们这里借…

Linux系统应用之知识补充——OpenEuler(欧拉)的安装和基础配置

前言 这篇文章将会对OpenEuler的安装进行详解&#xff0c;一步一步跟着走下去就可以成功 注意 &#xff1a;以下的指令操作最好在root权限下进行&#xff08;即su - root&#xff09; ☀️工贵其久&#xff0c;业贵其专&#xff01; 1、OpenEuler的安装 这里我不过多介绍&a…

相亲交友系统 现代爱情的导航仪

在这个数字化的时代&#xff0c;人们的生活方式发生了翻天覆地的变化&#xff0c;其中最显著的变化之一便是交友方式的转变。编辑h17711347205随着社会节奏的加快&#xff0c;越来越多的人选择通过相亲交友系统来寻找人生伴侣。相亲交友系统不仅简化了传统的交友流程&#xff0…

Java设计模式——工厂方法模式(完整详解,附有代码+案例)

文章目录 5.3 工厂方法模式5.3.1概述5.3.2 结构5.3.3 实现 5.3 工厂方法模式 针对5.2.3中的缺点&#xff0c;使用工厂方法模式就可以完美的解决&#xff0c;完全遵循开闭原则。 5.3.1概述 工厂方法模式&#xff1a;定义一个创建对象的接口&#xff08;这里的接口指的是工厂&…