【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器(下)

news/2024/9/28 23:28:00/

系列文章目录

【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器(上)
【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器(下)


文章目录

  • 系列文章目录
  • 前言
  • 一、装饰器语法
    • 6.@Builder
      • 语法:
        • (1)在组件内创建(按值传递):
        • (2)在组件外创建(按值传递)
        • (3)对象字面量入参形式(引用传递)
      • 示例1:
      • 示例2:
      • 示例3:
    • 7.@BuilderParam
      • 语法:
      • 示例1:
      • 示例2:
    • 8.@Styles
      • 语法:
      • 示例1:
      • 示例2:
    • 9.@Extend
      • 语法:
      • 示例:
    • 10.@LocalStorageProp和@LocalStorageLink
      • 语法:
      • 示例
    • 11.@StorageProp和@StorageLink
      • 语法:
      • 示例:
  • 二、 总结


前言

HarmonyOS NEXT(鸿蒙应用)开发快速入门教程ArkTS语法之装饰器篇(下),基于HarmonyOS NEXT Beta1版本(api 12)讲解。


一、装饰器语法

6.@Builder

自定义组件除了通过@Component申明方式构建,也可以通过函数方式构建。@Builder就是用来装饰函数定义一个轻量级组件(UI元素),因此@Builder装饰器也简称为自定义构建函数,返回一个组件或UI元素。类似vue的render函数,通过函数形式来构建UI元素。

语法:

(1)在组件内创建(按值传递):
@Component
struct Demo {@Builder 函数名(参数1:类型,参数2:类型,.....) {}}

调用:

this.函数名(参数1:类型,参数2:类型,.....)
(2)在组件外创建(按值传递)
@Builder function 函数名(参数1:类型,参数2:类型,.....) {
}@Component
struct Demo {}

调用:

函数名(参数1:类型,参数2:类型,.....)
(3)对象字面量入参形式(引用传递)
class ClassName{a:xxxxb:xxxxc:xxxx
}@Component
struct Demo {@Builder 函数名($$:ClassName) {}}

调用

函数名({a:value,b:value2,c:value3,.............
})

说明:
1、在组件内创建称为私有自定义构建函数,只有当前组件能使用,在组件外创建称为全局自定义构建函数,所有组件都能使用。
2、入参有2种写法,一种像上面示例传递多个形参方式,称为按值传递,另一种把多个参数写成对象字面量形式(只有一个入参)传入,这种叫按引用传递。按值传递只会在首次渲染时候有效,后续修改入参值将无法响应UI变化,而引用传递修改对象字面量属性值会响应UI变化,所以推荐入参全部写成对象字面量形式。

示例1:

按引用传递

class BuilderParams {image: Resource | string = ''label: string = ''
}@Entry
@Component
struct Parent {@State image: Resource = $r('app.media.home')//图标@State label: string = '首页'; //标签//自定义构建函数@BuilderchildBuilder(params: BuilderParams) {Column({ space: 6 }) {Image(params.image).width(100)Text(params.label).fontSize(16)}}build() {Column({ space: 30 }) {this.childBuilder({ image: this.image, label: this.label })Button('改变图标和标签').onClick(() => {this.image = $r('app.media.mine')this.label = '我的'})}.width('100%')}
}

运行效果:

请添加图片描述

示例2:

示例1也可以改成 如下直接访问外部状态变量,效果一样

@Entry
@Component
struct Parent {@State image: Resource = $r('app.media.home')//图标@State label: string = '首页'; //标签//自定义构建函数@BuilderchildBuilder() {Column({ space: 6 }) {Image(this.image).width(100)Text(this.label).fontSize(16)}}build() {Column({ space: 30 }) {this.childBuilder()Button('改变图标和标签').onClick(() => {this.image = $r('app.media.mine')this.label = '我的'})}.width('100%')}
}

示例3:

示例1也可以改成全局构建自定义函数,效果一样

class BuilderParams {image: Resource | string = ''label: string = ''
}//全局自定义构建函数
@Builder
function childBuilder(params: BuilderParams) {Column({ space: 6 }) {Image(params.image).width(100)Text(params.label).fontSize(16)}
}@Entry
@Component
struct Parent {@State image: Resource = $r('app.media.home')//图标@State label: string = '首页'; //标签build() {Column({ space: 30 }) {childBuilder({ image: this.image, label: this.label })Button('改变图标和标签').onClick(() => {this.image = $r('app.media.mine')this.label = '我的'})}.width('100%')}
}

@Builder和@Component都可以自定义组件,我们要怎么选择呢?

@Builder主要封装轻量级UI元素,组件参数传递只靠入参或者直接访问外部状态变量,无生命周期函数、无法使用其他装饰器功能,有开发的局限性,而@Component适用于构建复杂逻辑的组件,有自己的生命周期可以使用各种类型装饰器,可实现更复杂的场景。

实际开发根据自己的需求场景去选择,如果2种方式都满足,选择@Builder方式性能会更好。

7.@BuilderParam

@BuilderParam相当于vue的插槽作用(slot),先在子组件调用@BuilderParam修饰的函数占位,具体渲染内容从父组件传入,通过上面介绍我们知道@Builder是用来自定义一个组件,父组件通过@Builder生成的组件当参数传入子组件,子组件拿到@Builder组件在占位地方填充渲染。所以可以简单理解和记忆@BuilderParam是子组件的入参(函数类型),入参类型是@Builder函数。

语法:

子组件:

 @Builder A() {};@BuilderParam B:类型=this.Abuild(){......//插槽占位this.B()}

ps:@BuilderParam装饰的函数初始值必须设置且只能是@Builder装饰的函数

父组件:

//自定义插槽内容
@Builder C(){
}
build(){......child({B:this.C})}

如果只有一个插槽,父组件可以省略自定义构建函数,直接在子组件内嵌套写布局,此方式叫尾随闭包初始化组件,相当于vue中的默认插槽场景

父组件

build(){......child(){//自定义插槽内容............}}

示例1:

只有一个@BuilderParam

@Entry
@Component
struct Parent {@Builder customBuild(){//插槽内容Button('插槽按钮')}build() {Column({ space: 30 }) {Child({customBuilderParam:this.customBuild})}.width('100%')}
}@Component
struct  Child{@Builder customBuilder() {};@BuilderParam customBuilderParam:()=>void=this.customBuilderbuild() {Column(){Text('child组件')//插槽占位this.customBuilderParam()}}
}

示例2:

只有一个@BuilderParam可以写成尾随闭包方式:

@Entry
@Component
struct Parent {build() {Column({ space: 30 }) {Child(){//插槽内容Button('插槽按钮')}}.width('100%')}
}@Component
struct  Child{@Builder customBuilder() {};@BuilderParam customBuilderParam:()=>void=this.customBuilderbuild() {Column(){Text('child组件')//插槽占位this.customBuilderParam()}}
}

运行效果:
在这里插入图片描述

8.@Styles

@Styles用于装饰函数,函数内封装公共通用属性或通用事件,提供给需要的组件复用。类似scss里面的@mixin的作用

语法:

和@Builder类似@Styles可以定义在组件内或全局,在全局定义时需在方法名前面添加function关键字,组件内定义时则不需要添加function关键字

(1)全局:

@Styles function functionName() { ... }

调用:

组件(){}.functionName()

(2)组件内:

// 在组件内
@Component
struct Demo{@Styles functionName() {.height(100)}
}

调用:

组件(){}.functionName()

使用限制规则:
1、@Styles仅支持通用属性和通用事件
2、@Styles修饰的方法不支持入参
3、只能在当前文件内使用,不支持export导出给其他文件使用
4、组件内的@Styles优先级高于全局@Style

示例1:

全局定义

//全局公共样式
@Styles
function globalStyle() {.width('100%').height(100).padding(10).backgroundColor(Color.Green).border({ width: 1, color: Color.Black, style: BorderStyle.Solid }).borderRadius(10)
}@Entry
@Component
struct Parent {build() {Column({ space: 30 }) {Text('text1').fontColor(Color.White).globalStyle()Text('text2').fontColor(Color.White).globalStyle()Text('text3').fontColor(Color.White).globalStyle()}.width('100%').padding(20)}
}

运行效果:
在这里插入图片描述

示例2:

组件内定义

@Entry
@Component
struct Demo {@State bgColor: ResourceColor = Color.Yellow //背景色@State iHeight: number = 100 //高度//组件内样式@StylesglobalStyle() {.width('100%').height(this.iHeight).border({ width: 1, color: Color.Black, style: BorderStyle.Solid }).backgroundColor(this.bgColor).borderRadius(10).onClick(() => {//高度+10this.iHeight += 10})}//按钮样式@StylescustButtonStyle(){.width(200).height(80).onClick(() => {//切换背景色this.bgColor = this.bgColor === Color.Pink ? Color.Yellow : Color.Pink})}build() {Column({ space: 30 }) {Column() {Text('点击高度+10')}.justifyContent(FlexAlign.Center).globalStyle()Button('改变颜色').custButtonStyle()}.width('100%').padding(20)}
}

运行效果:

请添加图片描述

从上述示例看,尽管@Styles装饰的函数不能写入参数,但是我们可以把@Style定义在组件内,并通过this去访问组件内的状态变量,达到动态传参的效果。

9.@Extend

@Extend跟@Styles功能类似,都是用来抽离组件属性和事件,@Extend针对的是某个具体系统组件单独设置,单独使用,而@Styles所有组件通用。

语法:

@Extend(系统组件名) function functionName(入参可选) { ... }

使用规则:
1、和@Styles不同,@Extend仅支持在全局定义,不支持在组件内部定义。
2、和@Styles不同,@Extend支持封装指定组件的私有属性、私有事件和自身定义的全局方法。
3、和@Styles不同,@Extend装饰的方法支持参数,开发者可以在调用时传递参数,调用遵循TS方法传值调用。
4、只支持扩展原生组件,不支持自定义组件

示例:

/*** 自定义按钮样式* @param height:按钮高* @param width :按钮宽* @param backgourndColor :按钮背景色* @param type //按钮样式*/
@Extend(Button)
function customButton(height: number, width: number, backgourndColor: ResourceColor, type: ButtonType) {.height(height).width(width).type(type).backgroundColor(backgourndColor).fontColor(Color.White).fontSize(14)
}@Entry
@Component
struct Demo {build() {Column({ space: 30 }) {Button('提交').customButton(50, 300, Color.Blue, ButtonType.Capsule)Button('删除').customButton(60, 60, Color.Red, ButtonType.Circle)}.width('100%').padding(20)}
}

运行效果:

在这里插入图片描述


从以下开始的装饰器都是跟数据本地存储相关

10.@LocalStorageProp和@LocalStorageLink

@LocalStorageProp和@LocalStorageLink是页面级的UI状态存储,通过@Entry装饰器接收的参数可以在页面内共享同一个LocalStorage实例。

简单说每个注册的路由页面(@Entry装饰的组件)单独享有和管理一个LocalStorage实例,包括该页面引入的子孙组件共用同一个LocalStorage来存取数据。每个页面单独使用互不影响,默认不能相互访问,不能覆盖存储,关闭页面LocalStorage实例被系统回收数据被清除,类似web sessionStorage。

@LocalStorageProp(key)是和LocalStorage实例中key对应的属性建立单向数据同步。LocalStorage实例中key数据改变,@LocalStorageProp(key)装饰的变量也将跟着改变并能响应UI变化。
@LocalStorageLink(key)是和LocalStorage实例中key对应的属性建立双向数据同步,两者区别跟@Prop和@Link一样,一个数据单向流动一个双向绑定,其他作用一致。

语法:

创建LocalStorage实例:

let storage: LocalStorage = new LocalStorage();
storage.setOrCreate(key,value)//创建属性
storage.setOrCreate(key2,value2)//创建属性
storage.setOrCreate(key3,value3)//创建属性
...
...
@Entry(storage)//页面绑定LocalStorage
@Component
struct Parent {}

组件调用:

@Entry(storage)
@Component
struct Parent {@LocalStorageProp(key) 变量:类型=默认值 @LocalStorageLink(key) 变量:类型=默认值 
}

修改值:
分两种方式,如果是@LocalStorageLink(key)双向绑定只要修改@LocalStorageLink绑定变量即可。另一种通过调用LocalStorage实例的方法修改

storage.set(key,value)

也可以通过storage获取值:

storage.get(key)

判断key是否存在

storage.has(key)

删除LocalStorage中所有的属性

storage.clear()

更多的api可以查看LocalStorage9+文档

示例


//城市地区对象
class AddressProp{city:stringarea:stringconstructor(city:string,area:string) {this.city=citythis.area=area}
}let storage: LocalStorage = new LocalStorage();
storage.setOrCreate('name','李磊')
storage.setOrCreate('age',0)
storage.setOrCreate('address',new AddressProp('厦门市','思明区'))@Entry(storage)
@Component
struct Parent {@LocalStorageProp('name') name:string='' //姓名@LocalStorageLink('age') age:number=0 //年龄@LocalStorageLink('address') address:AddressProp=new AddressProp('','') //地址build() {Column({ space: 20 }) {Text(`姓名:${this.name}`)Text(`年龄:${this.age}`)//子组件Child()Button('修改姓名').onClick(()=>{storage.set('name','小明')})Button('修改年龄').onClick(()=>{this.age=20})Button('修改城市').onClick(()=>{this.address.city='福州市'})Button('修改地区').onClick(()=>{this.address.area='鼓楼区'})Button('修改城市+地区').onClick(()=>{storage.set('address',new AddressProp('泉州市','丰泽区'))})}.padding(20).alignItems(HorizontalAlign.Start)}
}//子组件
@Component
struct Child {@LocalStorageLink('address') address:AddressProp=new AddressProp('','')build() {Column({space:20}){Text(`地址:${this.address.city}${this.address.area}`)}}
}

运行效果:
请添加图片描述

11.@StorageProp和@StorageLink

@StorageProp和@StorageLink是应用全局的UI状态存储,与之对应的是全局静态类AppStorage。和进程绑定,同一进程下的所有页面共用,生命周期和LocalStorage类似,当关闭应用将被回收,如果想要数据持久化本地保存需要结合PersistentStorage使用。

语法:

@Entry
@Component
struct Parent {@StorageLink(key) 变量:类型=默认值 @StorageProp(key) 变量:类型=默认值 
}

无需创建实例,直接使用

AppStorage和LocalStorage实例内置方法一样

AppStorage.setOrCreate(key,value) //创建属性
AppStorage.set(key,value) //设置值
AppStorage.get(key) //获取值
AppStorage.has(key)//判断是否存在属性
AppStorage.clear()//清除所有属性

示例:

Index.ets (第一个页面)

import { router } from '@kit.ArkUI'//商品对象类型
export class GoodsProp{name:stringprice:numberimage:Resource|stringconstructor(name:string,price:number,image:Resource|string) {this.name=namethis.price=pricethis.image=image}
}
//创建属性并设置初始值
AppStorage.setOrCreate('category','水果')
AppStorage.setOrCreate('place','海南')
AppStorage.setOrCreate('goods',new GoodsProp('榴莲',200,'https://img0.baidu.com/it/u=211945568,1824960805&fm=253&fmt=auto&app=120&f=JPEG?w=399&h=399'))@Entry
@Component
struct Demo {@StorageProp('category') category:string=''@StorageLink('place') place:string=''@StorageLink('goods') goods:GoodsProp=new GoodsProp('',0,'')build() {Column({ space: 20 }) {Text(`类别:${this.category}`)Text(`产地:${this.place}`)Text(`名称:${this.goods.name}`)Text(`价格:¥${this.goods.price}`)Image(this.goods.image).width(100)Button('修改商品信息').onClick(()=>{//修改商品信息AppStorage.set('category','饮料')this.place='福建'this.goods=new GoodsProp('雪津啤酒',10,'https://img1.baidu.com/it/u=3892212987,1352825001&fm=253&fmt=auto&app=138&f=JPEG?w=283&h=800')})Button('跳转下一页').onClick(()=>{router.pushUrl({url:'pages/Second'})})}.padding(20).alignItems(HorizontalAlign.Start)}
}

Second.ets(第二个页面)

import {GoodsProp} from './Index'
import { router } from '@kit.ArkUI'@Entry
@Component
struct  Second{@StorageLink('category') category:string=''@StorageProp('place') place:string=''@StorageProp('goods') goods:GoodsProp=new GoodsProp('',0,'')build() {Column({ space: 20 }) {Text(`类别:${this.category}`)Text(`产地:${this.place}`)Text(`名称:${this.goods.name}`)Text(`价格:¥${this.goods.price}`)Image(this.goods.image).width(100)Button('修改商品信息').onClick(()=>{//修改商品信息this.category="食物"AppStorage.set('place','北京')AppStorage.set('goods',new GoodsProp('北京烤鸭',50,'https://img2.baidu.com/it/u=543904622,2448295859&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=667'))})Button('返回上一页').onClick(()=>{router.back()})}.padding(20).alignItems(HorizontalAlign.Start)}
}

运行效果:

请添加图片描述

想要持久化数据只需页面开头用PersistentStorage.persistProp(key,value)初始化属性即可

例如上个例子改为:
Index.ets:

import { router } from '@kit.ArkUI'//商品对象类型
export class GoodsProp{name:stringprice:numberimage:Resource|stringconstructor(name:string,price:number,image:Resource|string) {this.name=namethis.price=pricethis.image=image}
}PersistentStorage.persistProp('category','水果')
PersistentStorage.persistProp('place','海南')
PersistentStorage.persistProp('goods',new GoodsProp('榴莲',200,'https://img0.baidu.com/it/u=211945568,1824960805&fm=253&fmt=auto&app=120&f=JPEG?w=399&h=399'))@Entry
@Component
struct Demo {@StorageProp('category') category:string=''@StorageLink('place') place:string=''@StorageLink('goods') goods:GoodsProp=new GoodsProp('',0,'')build() {Column({ space: 20 }) {Text(`类别:${this.category}`)Text(`产地:${this.place}`)Text(`名称:${this.goods.name}`)Text(`价格:¥${this.goods.price}`)Image(this.goods.image).width(100)Button('修改商品信息').onClick(()=>{//修改商品信息AppStorage.set('category','饮料')this.place='福建'this.goods=new GoodsProp('雪津啤酒',10,'https://img1.baidu.com/it/u=3892212987,1352825001&fm=253&fmt=auto&app=138&f=JPEG?w=283&h=800')})Button('跳转下一页').onClick(()=>{router.pushUrl({url:'pages/Second'})})}.padding(20).alignItems(HorizontalAlign.Start)}
}

二、 总结

至此11个常用的装饰器已经讲完了,完全掌握你将能从容应对大部分的需求场景开发。v2版本官方也在加速推进开发中,我们完全能期待下一个版本正式发布,因为v2版本又会新增多个实用装饰器,改进v1版本不足将带来更加便捷的开发方式。例如有类似vue中的compute计算属性装饰器、类似@click装饰事件的装饰器,以及对对象深度监听支持等。


http://www.ppmy.cn/news/1530749.html

相关文章

PMP--二模--解题--111-120

文章目录 7.成本管理111、 [单选] 你向项目出资人提供了项目的成本估算,他对估算不满意,因为他认为价格太高了。他要你削减项目估算的15%,你该怎么做? 8.质量管理112、 [单选] 在新建水处理厂的建设过程中,政府对处理厂…

文件上传漏洞+CTF实例

解题思路 前端绕过 手动修改前端js代码进行绕过:右击-查看页面源代码-ctff进行位置定位-修改JavaScript函数 后端绕过 文件类型绕过(Content-Type) 常见MIME类型描述application/octet-stream 表示所有其他情况的默认值 text/plain表示文…

节日拜访:白酒作为节日礼物,传递节日祝福

随着节日的脚步渐近,人们开始忙碌地挑选礼物,准备拜访亲朋好友。在这个欢聚的时刻,选择一款合适的节日礼物显得尤为重要。而豪迈白酒(HOMANLISM),作为一款品质且富有文化内涵的白酒,正逐渐成为人…

代码随想录算法训练营第58天|卡码网 117. 软件构建、47. 参加科学大会

1. 卡码网 117. 软件构建 题目链接:https://kamacoder.com/problempage.php?pid1191 文章链接:https://www.programmercarl.com/kamacoder/0117.软件构建.html 思路:使用BFS BFS的实现思路: 拓扑排序的过程,其实就两步…

Linux、Windows、Android下查看可执行文件、动态库和静态库信息的命令

TL; DR 我常用的命令: Linux lddWindows(需要借助vs) dumpbin /DEPENDENTSAndroid ldd 绝对路径在不同的操作系统下,查看可执行文件、动态库和静态库的命令各不相同。以下是 Linux、Windows 和 Android 平台下的常用命令&…

开源推理库介绍:ZML,Distributed Llama,EXO | LeetTalk Daily

“LeetTalk Daily”,每日科技前沿,由LeetTools AI精心筛选,为您带来最新鲜、最具洞察力的科技新闻。 开源推理库的出现为机器学习模型的部署、监控和扩展提供了强大的支持。我们介绍三个重要的开源推理库:ZML、Distributed Llama …

LiveQing视频点播流媒体RTMP推流服务功能-支持电子放大拉框放大直播视频拉框放大录像视频流拉框放大电子放大

LiveQing视频点播流媒体RTMP推流服务功能-支持电子放大拉框放大直播视频拉框放大录像视频流拉框放大电子放大 1、鉴权直播2、视频点播3、RTMP推流视频直播和点播流媒体服务 1、鉴权直播 鉴权直播-》播放 ,左键单击可以拉取矩形框,放大选中的范围&#x…

react crash course 2024(2) 创建项目及vscode插件

使用vite创建react项目 npm create vitelatest react-crash-2024 跳到那个项目 cd .\react-crash-2024 打开那个项目 code . 在vite.config.js中设置端口 安装依赖 npm i 运行 npm run dev vs code插件 rafce //在底部导出的react箭头函数组件