页面跳转
跳转
跳转到第二个页面:
import { router } from '@kit.ArkUI'
// ....
Button() {
Text("Next")
}
.type(ButtonType.Normal)
.onClick(event => {
console.log('button click');
router.pushUrl({
url: 'pages/Second'
}).then(() => {
console.info('Succeeded in jumping to the second page.')
}).catch((err: BusinessError) => {
console.error(`Failed to jump to the second page. Code is ${err.code}, message is ${err.message}`)
})
})
import { router } from '@kit.ArkUI'
// ....
Button() {
Text("Next")
}
.type(ButtonType.Normal)
.onClick(event => {
console.log('button click');
router.pushUrl({
url: 'pages/Second'
}).then(() => {
console.info('Succeeded in jumping to the second page.')
}).catch((err: BusinessError) => {
console.error(`Failed to jump to the second page. Code is ${err.code}, message is ${err.message}`)
})
})
注意第二个页面需要注册路由(entry > src > main > resources > base > profile
):
页面传参
页面返回
import { router } from '@kit.ArkUI'
router.back();
import { router } from '@kit.ArkUI'
router.back();
页面参数回调
可以通过函数闭包的方式处理。
Navigation跳转
TODO
生命周期
UIAbility的生命周期
UIAbility的生命周期包括Create、Foreground、Background、Destroy四个状态。
@Component组件的生命周期
- aboutToAppear:组件即将出现时回调该接口,具体时机为在创建自定义组件的新实例后,在执行其build()函数之前执行。
- onDidBuild:组件build()函数执行完成之后回调该接口,不建议在onDidBuild函数中更改状态变量、使用animateTo等功能,这可能会导致不稳定的UI表现。
- aboutToDisappear:aboutToDisappear函数在自定义组件析构销毁之前执行。不允许在aboutToDisappear函数中改变状态变量,特别是@Link变量的修改可能会导致应用程序行为不稳定。另外不建议在生命周期aboutToDisappear内使用async await。
@Entry组件/页面的生命周期
- onPageShow:页面每次显示时触发一次,包括路由过程、应用进入前台等场景。
- onPageHide:页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景。
- onBackPress:当用户点击返回按钮时触发。
无感监听页面路由
使用observer监听。
如何自定义组件
- 自定义组件的成员函数为私有的,且不建议声明成静态函数
- 状态管理相关文档:状态管理概述
父组件引用子组件
虽然声明的是private,但仍然支持传递参数。
示例代码:
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
@State color: Color = Color.Red
build() {
Row() {
SubComponent({ color: this.color, text: 'this is a text' })
}
}
}
@Component
struct SubComponent {
private color: Color = Color.Green
private text = ''
build() {
Text(this.text)
.fontSize(40)
.foregroundColor(this.color)
}
}
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
@State color: Color = Color.Red
build() {
Row() {
SubComponent({ color: this.color, text: 'this is a text' })
}
}
}
@Component
struct SubComponent {
private color: Color = Color.Green
private text = ''
build() {
Text(this.text)
.fontSize(40)
.foregroundColor(this.color)
}
}
父组件更新子组件
@Prop装饰的变量可以和父组件建立单向同步关系,@Prop装饰的变量是可变的,但修改不会同步回父组件。
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
@State color: Color = Color.Red
build() {
Row() {
SubComponent({ color: this.color, text: 'this is a text' })
Button() {
Text('yellow')
.foregroundColor(Color.White)
.padding(4)
}
.onClick(() => {
this.color = Color.Yellow
})
}
}
}
@Component
struct SubComponent {
@Prop color: Color = Color.Green
private text = ''
build() {
Text(this.text)
.fontSize(40)
.foregroundColor(this.color)
.onClick(() => {
// 不会更新到父组件
this.color = Color.Orange
})
}
}
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
@State color: Color = Color.Red
build() {
Row() {
SubComponent({ color: this.color, text: 'this is a text' })
Button() {
Text('yellow')
.foregroundColor(Color.White)
.padding(4)
}
.onClick(() => {
this.color = Color.Yellow
})
}
}
}
@Component
struct SubComponent {
@Prop color: Color = Color.Green
private text = ''
build() {
Text(this.text)
.fontSize(40)
.foregroundColor(this.color)
.onClick(() => {
// 不会更新到父组件
this.color = Color.Orange
})
}
}
双向绑定
参照 #link-状态管理
父组件调用子组件的方法
// 参考: https://developer.huawei.com/consumer/cn/forum/topic/0202140470508418066?fid=0102683795438680754
声明:
export class ComponentRef<T> {
value ?: T
export(ref: T) {
this.value = ref
}
static useRef<T>() {
return new ComponentRef<T>()
}
}
export type CommonRefInterface = Record<string, Function>; // 示例,未使用
export interface WebComponentRef {
reloadWebView : Function
}
export class ComponentRef<T> {
value ?: T
export(ref: T) {
this.value = ref
}
static useRef<T>() {
return new ComponentRef<T>()
}
}
export type CommonRefInterface = Record<string, Function>; // 示例,未使用
export interface WebComponentRef {
reloadWebView : Function
}
父组件:
childrenRef = ComponentRef.useRef<WebComponentRef>();
build() {
childComponent({ url: this.homeUrl, title: this.h5Title, childrenRef: this.childrenRef })
}
aboutToAppear(): void {
this.eventHub.on(EventTypeEnum.RELOAD_HOME, async () => {
this.childrenRef.value?.reloadWebView()
})
}
childrenRef = ComponentRef.useRef<WebComponentRef>();
build() {
childComponent({ url: this.homeUrl, title: this.h5Title, childrenRef: this.childrenRef })
}
aboutToAppear(): void {
this.eventHub.on(EventTypeEnum.RELOAD_HOME, async () => {
this.childrenRef.value?.reloadWebView()
})
}
子组件:
childrenRef: ComponentRef<WebComponentRef>| null = null
async aboutToAppear() {
this.childrenRef?.export({
reloadWebView: () => {
// 子组件需要调用的方法
}
})
}
childrenRef: ComponentRef<WebComponentRef>| null = null
async aboutToAppear() {
this.childrenRef?.export({
reloadWebView: () => {
// 子组件需要调用的方法
}
})
}
修饰器说明
@Entry
入口组件,一个文件只能有一个Entry
说明
从API version 9开始,该装饰器支持在ArkTS卡片中使用。
从API version 10开始,@Entry可以接受一个可选的LocalStorage的参数或者一个可选的EntryOptions参数。
从API version 11开始,该装饰器支持在元服务中使用。
参数 routeName
可以用于Router可以通过命名路由的方式跳转。 可参考文档: Router切换Navigation-设置组件导航和页面路由
参数 storage
页面级的UI状态存储。
参数 useSharedStorage
是否使用LocalStorage.getShared()接口返回的LocalStorage实例对象,默认值false。
@Component
自定义组件。 只能修饰struct关键字声明的数据结构。
- freezeWhenInactive 是否开启组件冻结。自定义组件处于非激活状态时,状态变量将不响应更新,即@Watch不会调用,状态变量关联的节点不会刷新。
@Reusable
让自定义的组件具备可复用能力。
@State 状态管理
表示组件中的状态变量,状态改变时触发ui刷新。
@State装饰的变量,与声明式范式中的其他被装饰变量一样,是私有的,只能从组件内部访问,在声明时必须指定其类型和本地初始化。初始化也可选择使用命名参数机制从父组件完成初始化。
示例代码见父组件更新子组件
支持修饰: 简单类型、class类型(例如修饰一个viewModel)、map类型。
参考文档:
@State装饰器
@Prop 状态管理
@Prop装饰的变量可以和父组件建立单向的同步关系。@Prop装饰的变量是可变的,但是变化不会同步回其父组件。
- 传递的时候数据会进行深拷贝
- 不支持修饰any,支持undefined和null
@Link 状态管理
用于双向同步。
- 父组件用 @State,@StorageLink和@Link修饰,子组件用 @Link可以建立双向数据同步
- 不要初始化@Link修饰的内容
@Entry
@Component
struct Index {
@State color: Color = Color.Red
build() {
Row() {
SubComponent({ color: this.color, text: 'this is a text' })
Button() {
Text(this.color.toString())
.foregroundColor(Color.White)
.padding(4)
}
.onClick(() => {
this.color = Color.Yellow
})
}
}
}
@Component
struct SubComponent {
@Link color: Color
private text = ''
build() {
Text(this.text)
.fontSize(40)
.foregroundColor(this.color)
.onClick(() => {
// 支持更新到父组件
this.color = Color.Orange
})
}
}
@Entry
@Component
struct Index {
@State color: Color = Color.Red
build() {
Row() {
SubComponent({ color: this.color, text: 'this is a text' })
Button() {
Text(this.color.toString())
.foregroundColor(Color.White)
.padding(4)
}
.onClick(() => {
this.color = Color.Yellow
})
}
}
}
@Component
struct SubComponent {
@Link color: Color
private text = ''
build() {
Text(this.text)
.fontSize(40)
.foregroundColor(this.color)
.onClick(() => {
// 支持更新到父组件
this.color = Color.Orange
})
}
}
@Provide/@Consume 状态管理
@Provide和@Consume,应用于与后代组件的双向数据同步。 有点类似Vue中Provide/Inject。
- @Provide和@Consume可以通过相同的变量名或者相同的变量别名绑定,建议类型相同,否则会发生类型隐式转换,从而导致应用行为异常。
- @Provide 被装饰变量的初始值必须指定
- @Provide 被装饰变量的初始值禁止初始化
// 通过相同的变量名绑定
@Provide a: number = 0;
@Consume a: number;
// 通过相同的变量别名绑定
@Provide('a') b: number = 0;
@Consume('a') c: number;
// 通过相同的变量名绑定
@Provide a: number = 0;
@Consume a: number;
// 通过相同的变量别名绑定
@Provide('a') b: number = 0;
@Consume('a') c: number;
@Observed/@ObjectLink 状态管理
弥补深度嵌套场景的数据监听问题。
- 使用@Observed装饰class会改变class原始的原型链,@Observed和其他类装饰器装饰同一个class可能会带来问题。
- @ObjectLink装饰器不能在@Entry装饰的自定义组件中使用。
- @ObjectLink装饰的变量不能被赋值(只有成员变量可以赋值),如果要使用赋值操作,请使用@Prop。
@Watch
略
@StorageLink
@Builder
修饰于函数,用于自定义构建UI。
例如:
class Tmp {
paramA1: string = ''
}
@Builder function overBuilder(params: Tmp) {
Row() {
Text(`UseStateVarByReference: ${params.paramA1} `)
}
}
class Tmp {
paramA1: string = ''
}
@Builder function overBuilder(params: Tmp) {
Row() {
Text(`UseStateVarByReference: ${params.paramA1} `)
}
}
按引用传递参数时,如果在@Builder方法内调用自定义组件,ArkUI提供$$作为按引用传递参数的范式。
class Tmp {
paramA1: string = ''
}
@Builder function overBuilder($$: Tmp) {
Row() {
Column() {
Text(`overBuilder===${$$.paramA1}`)
HelloComponent({message: $$.paramA1})
}
}
}
@Component
struct HelloComponent {
@Prop message: string;
build() {
Row() {
Text(`HelloComponent===${this.message}`)
}
}
}
/// 父组件调用:
@State label: string = 'Hello';
overBuilder({paramA1: this.label})
class Tmp {
paramA1: string = ''
}
@Builder function overBuilder($$: Tmp) {
Row() {
Column() {
Text(`overBuilder===${$$.paramA1}`)
HelloComponent({message: $$.paramA1})
}
}
}
@Component
struct HelloComponent {
@Prop message: string;
build() {
Row() {
Text(`HelloComponent===${this.message}`)
}
}
}
/// 父组件调用:
@State label: string = 'Hello';
overBuilder({paramA1: this.label})
@Preview
增加以支持单独预览单个小部件
Q&A
console 从哪里看
稍微有点隐蔽,需要点击底部Log标签查看。
官方有哪些示例
如何进行接口调用
viewModel的示例
@State装饰器:组件内状态-管理组件拥有的状态-状态管理(V1稳定版)-状态管理-学习ArkTS语言-基础入门 - 华为HarmonyOS开发者
function中的this指向问题
稍微注意下this的指向问题,函数中this只在乎谁调用的它。
// 方案1:推荐
Button('Click me')
.onClick(() => {
this.myText = 'ArkUI';
})
// 方案2:可用,不推荐
myClickHandler(): void {
this.counter += 2;
}
...
Button('add counter')
.onClick(this.myClickHandler.bind(this))
// 方案3: 可用
fn = () => {
console.info(`counter: ${this.counter}`)
this.counter++
}
...
Button('add counter')
.onClick(this.fn)
// 方案1:推荐
Button('Click me')
.onClick(() => {
this.myText = 'ArkUI';
})
// 方案2:可用,不推荐
myClickHandler(): void {
this.counter += 2;
}
...
Button('add counter')
.onClick(this.myClickHandler.bind(this))
// 方案3: 可用
fn = () => {
console.info(`counter: ${this.counter}`)
this.counter++
}
...
Button('add counter')
.onClick(this.fn)
Foreach渲染注意事项
foreach生成的键值用来标识唯一的对象。
- ForEach提供了一个名为keyGenerator的参数,这是一个函数,开发者可以通过它自定义键值的生成规则。
- 如果开发者没有定义keyGenerator函数,则ArkUI框架会使用默认的键值生成函数,即:
(item: Object, index: number) =>
{ return index + '__' + JSON.stringify(item);
}
(item: Object, index: number) =>
{ return index + '__' + JSON.stringify(item);
}
例如
@Component
struct SubComponent {
private text = ''
number: number[] = [1, 2, 3]
build() {
Row() {
ForEach(this.number, (item: number) => {
Text(item + '')
}, (item: number) => item + '')
}
}
}
@Component
struct SubComponent {
private text = ''
number: number[] = [1, 2, 3]
build() {
Row() {
ForEach(this.number, (item: number) => {
Text(item + '')
}, (item: number) => item + '')
}
}
}
如何进行资源加载
资源分类简介:
├── AppScope
│ ├── app.json5
│ └── resources
│ └── base // 默认存在
│ ├── element // 存放字符串、颜色、布尔值等基础元素
│ │ └── string.json
│ └── media // 表示媒体资源,包括图片、音视频等非文本格式的文件
│ └── app_icon.png
├── build-profile.json5
├── code-linter.json5
├── entry
│ ├── build
│ ├── ....
│ └── src
│ ├── main
│ │ ├── ...
│ │ ├── module.json5
│ │ └── resources
│ │ ├── base
│ │ │ ├── element // 存放字符串、颜色、布尔值等基础元素
│ │ │ │ ├── color.json
│ │ │ │ └── string.json
│ │ │ ├── media // 媒体文件
│ │ │ │ ├── background.png
│ │ │ │ ├── foreground.png
│ │ │ │ ├── layered_image.json
│ │ │ │ └── startIcon.png
│ │ │ └── profile // 自定义配置文件,必须json格式
│ │ │ ├── backup_config.json
│ │ │ └── main_pages.json
│ │ ├── en_US
│ │ │ └── element
│ │ │ └── string.json
│ │ ├── rawfile // 其他类型文件,原始文件形式保存
│ │ └── zh_CN
│ │ └── element
│ │ └── string.json
├── AppScope
│ ├── app.json5
│ └── resources
│ └── base // 默认存在
│ ├── element // 存放字符串、颜色、布尔值等基础元素
│ │ └── string.json
│ └── media // 表示媒体资源,包括图片、音视频等非文本格式的文件
│ └── app_icon.png
├── build-profile.json5
├── code-linter.json5
├── entry
│ ├── build
│ ├── ....
│ └── src
│ ├── main
│ │ ├── ...
│ │ ├── module.json5
│ │ └── resources
│ │ ├── base
│ │ │ ├── element // 存放字符串、颜色、布尔值等基础元素
│ │ │ │ ├── color.json
│ │ │ │ └── string.json
│ │ │ ├── media // 媒体文件
│ │ │ │ ├── background.png
│ │ │ │ ├── foreground.png
│ │ │ │ ├── layered_image.json
│ │ │ │ └── startIcon.png
│ │ │ └── profile // 自定义配置文件,必须json格式
│ │ │ ├── backup_config.json
│ │ │ └── main_pages.json
│ │ ├── en_US
│ │ │ └── element
│ │ │ └── string.json
│ │ ├── rawfile // 其他类型文件,原始文件形式保存
│ │ └── zh_CN
│ │ └── element
│ │ └── string.json
如何进行多module开发
略
开发时如何进行数据mock
考虑也是单独套一个@Preview修饰的组件,然后数据写死。用于快速开发预览。
调试包如何安装到真机
hdc app install -r xxxx.hap
hdc app install -r xxxx.hap
如果生产包,需要走应用测试
,类似苹果的testflight。