当前位置: 首页 > news >正文

HarmonyOS Next 实战技巧集锦

HarmonyOS Next 实战技巧集锦

基于 HealthyMate 项目的进阶开发技巧

前置要求: 已阅读《HarmonyNext学习指南-完整版.md》


📑 目录

  • 1. 组件通信模式
  • 2. 自定义构建函数进阶
  • 3. 样式复用技巧
  • 4. 列表优化
  • 5. 表单处理
  • 6. 动画效果
  • 7. 资源管理
  • 8. 调试技巧
  • 9. 项目实战案例

1. 组件通信模式

1.1 父传子:@Prop

// 父组件
@Entry
@Component
struct ParentPage {@State userInfo: UserInfo = {name: '张三',age: 25}build() {Column() {UserCard({ user: this.userInfo })}}
}// 子组件
@Component
struct UserCard {@Prop user: UserInfobuild() {Column() {Text(this.user.name)Text(this.user.age.toString())}}
}

注意事项:

  • ✅ 简单类型(string, number)直接传递
  • ✅ 对象类型会深拷贝
  • ⚠️ 子组件修改不影响父组件

1.2 子传父:回调函数

// 父组件
@Entry
@Component
struct ParentPage {@State count: number = 0handleIncrement = () => {this.count++}build() {Column() {Text(`父组件计数: ${this.count}`)CounterChild({ onIncrement: this.handleIncrement })}}
}// 子组件
@Component
struct CounterChild {onIncrement?: () => voidbuild() {Button('增加').onClick(() => {this.onIncrement?.()  // 调用父组件传入的回调})}
}

1.3 双向绑定:@Link

// 父组件
@Entry
@Component
struct ParentPage {@State inputValue: string = ''build() {Column() {Text(`输入值: ${this.inputValue}`)// 使用 $ 传递引用InputField({ value: $inputValue })}}
}// 子组件
@Component
struct InputField {@Link value: string  // 双向绑定build() {TextInput({ text: this.value }).onChange((text: string) => {this.value = text  // 修改会同步到父组件})}
}

1.4 跨组件通信:AppStorage

// 设置全局数据(任意位置)
AppStorage.setOrCreate('userToken', 'abc123')
AppStorage.setOrCreate('isLoggedIn', true)// 组件A: 写入数据
@Component
struct ComponentA {handleLogin() {AppStorage.set('isLoggedIn', true)AppStorage.set('userToken', 'new_token')}build() {Button('登录').onClick(() => this.handleLogin())}
}// 组件B: 读取数据(不同页面)
@Component
struct ComponentB {@StorageLink('isLoggedIn') isLoggedIn: boolean = false@StorageLink('userToken') token: string = ''build() {Column() {if (this.isLoggedIn) {Text(`已登录,Token: ${this.token}`)} else {Text('未登录')}}}
}

1.5 事件总线模式(自定义)

// 事件总线工具类
class EventBus {private listeners: Map<string, Function[]> = new Map()// 订阅事件on(event: string, callback: Function) {if (!this.listeners.has(event)) {this.listeners.set(event, [])}this.listeners.get(event)?.push(callback)}// 取消订阅off(event: string, callback: Function) {const callbacks = this.listeners.get(event)if (callbacks) {const index = callbacks.indexOf(callback)if (index > -1) {callbacks.splice(index, 1)}}}// 触发事件emit(event: string, ...args: any[]) {const callbacks = this.listeners.get(event)if (callbacks) {callbacks.forEach(callback => callback(...args))}}
}// 创建全局实例
export const eventBus = new EventBus()// 使用示例
// 组件A: 订阅事件
@Component
struct ComponentA {@State message: string = ''aboutToAppear() {eventBus.on('userLogin', (username: string) => {this.message = `${username} 已登录`})}build() {Text(this.message)}
}// 组件B: 触发事件
@Component
struct ComponentB {handleLogin() {eventBus.emit('userLogin', '张三')}build() {Button('登录').onClick(() => this.handleLogin())}
}

2. 自定义构建函数进阶

2.1 @Builder 基础用法

@Component
struct BuilderDemo {// 组件内部 @Builder@BuilderheaderBuilder() {Row() {Image($r('app.media.icon'))Text('标题')}}build() {Column() {this.headerBuilder()  // 调用}}
}// 全局 @Builder
@Builder
function globalHeader() {Row() {Text('全局标题')}
}@Component
struct AnotherComponent {build() {Column() {globalHeader()  // 调用全局 Builder}}
}

2.2 @Builder 传参

@Component
struct BuilderWithParams {@State items: string[] = ['项目1', '项目2', '项目3']// 传递对象参数@BuilderlistItemBuilder(item: { text: string, index: number }) {Row() {Text(`${item.index + 1}. ${item.text}`)}.padding(10).backgroundColor('#F0F0F0').margin(5)}build() {Column() {ForEach(this.items, (item: string, index: number) => {// 传递对象字面量this.listItemBuilder({ text: item, index: index })})}}
}

重要规则:

  • ✅ 传递单个参数:使用对象字面量
  • ✅ 对象字面量的值改变会触发UI刷新
  • ❌ 传递多个独立参数不支持

2.3 @BuilderParam 插槽

// 定义带插槽的卡片组件
@Component
struct SlotCard {@Prop title: string// 插槽1: 内容区@BuilderParam contentSlot: () => void = this.defaultContent// 插槽2: 底部按钮@BuilderParam actionSlot: () => void = this.defaultAction@BuilderdefaultContent() {Text('默认内容')}@BuilderdefaultAction() {Button('默认按钮')}build() {Column() {// 标题Text(this.title).fontSize(20).fontWeight(FontWeight.Bold)Divider()// 内容插槽this.contentSlot()Divider()// 操作插槽this.actionSlot()}.padding(16).borderRadius(12).backgroundColor(Color.White)}
}// 使用插槽
@Entry
@Component
struct SlotDemo {@BuildercustomContent() {Column() {Image($r('app.media.icon')).width(100)Text('自定义内容区域')}}@BuildercustomActions() {Row() {Button('取消')Button('确定')}}build() {Column() {SlotCard({title: '自定义卡片',contentSlot: this.customContent,actionSlot: this.customActions})}}
}

2.4 多态插槽(根据条件渲染不同内容)

@Component
struct PolymorphicCard {@Prop cardType: 'info' | 'warning' | 'error'@BuilderParam contentBuilder: () => void// 根据类型返回不同图标getIcon(): ResourceStr {switch (this.cardType) {case 'info':return $r('app.media.icon_info')case 'warning':return $r('app.media.icon_warning')case 'error':return $r('app.media.icon_error')default:return $r('app.media.icon_info')}}// 根据类型返回不同颜色getColor(): string {switch (this.cardType) {case 'info':return '#4D91FF'case 'warning':return '#FFA500'case 'error':return '#FF3D71'default:return '#4D91FF'}}build() {Row() {Image(this.getIcon()).width(24).fillColor(this.getColor())this.contentBuilder()}.padding(12).borderRadius(8).border({ width: 1, color: this.getColor() })}
}// 使用
@Entry
@Component
struct PolymorphicDemo {build() {Column({ space: 10 }) {PolymorphicCard({cardType: 'info',contentBuilder: () => {Text('这是信息提示')}})PolymorphicCard({cardType: 'warning',contentBuilder: () => {Text('这是警告提示')}})PolymorphicCard({cardType: 'error',contentBuilder: () => {Text('这是错误提示')}})}}
}

3. 样式复用技巧

3.1 @Extend 样式扩展

// 定义文本样式
@Extend(Text)
function primaryText() {.fontSize(16).fontColor('#333333').fontWeight(FontWeight.Normal)
}@Extend(Text)
function titleText() {.fontSize(24).fontColor('#000000').fontWeight(FontWeight.Bold)
}@Extend(Text)
function captionText() {.fontSize(12).fontColor('#999999')
}// 定义按钮样式
@Extend(Button)
function primaryButton() {.backgroundColor('#4D91FF').fontColor(Color.White).borderRadius(8).padding({ left: 20, right: 20, top: 10, bottom: 10 })
}@Extend(Button)
function secondaryButton() {.backgroundColor(Color.White).fontColor('#4D91FF').borderRadius(8).border({ width: 1, color: '#4D91FF' }).padding({ left: 20, right: 20, top: 10, bottom: 10 })
}// 使用
@Component
struct StyleDemo {build() {Column() {Text('标题').titleText()Text('正文内容').primaryText()Text('说明文字').captionText()Button('主要按钮').primaryButton()Button('次要按钮').secondaryButton()}}
}

3.2 @Extend 带参数

// 带参数的样式扩展
@Extend(Text)
function customText(size: number, color: string, weight: FontWeight) {.fontSize(size).fontColor(color).fontWeight(weight)
}@Extend(Button)
function roundButton(bgColor: string, radius: number) {.backgroundColor(bgColor).borderRadius(radius).padding({ left: 16, right: 16, top: 8, bottom: 8 })
}// 使用
@Component
struct ParamStyleDemo {build() {Column() {Text('自定义文本').customText(18, '#FF0000', FontWeight.Bold)Button('圆角按钮').roundButton('#4D91FF', 20)}}
}

3.3 @Styles 样式集合

// 定义样式集合
@Styles
function cardStyle() {.backgroundColor(Color.White).borderRadius(12).padding(16).shadow({radius: 10,color: '#00000020',offsetX: 0,offsetY: 2})
}@Styles
function flexCenterStyle() {.width('100%').justifyContent(FlexAlign.Center).alignItems(ItemAlign.Center)
}// 使用
@Component
struct StylesDemo {build() {Column() {Column() {Text('卡片内容')}.cardStyle()  // 应用样式集合Row() {Text('居中内容')}.flexCenterStyle()}}
}

3.4 全局样式文件

创建文件: pages/common/GlobalStyles.ets

// 全局文本样式
@Extend(Text)
export function h1() {.fontSize(32).fontColor('#000000').fontWeight(FontWeight.Bold)
}@Extend(Text)
export function h2() {.fontSize(24).fontColor('#000000').fontWeight(FontWeight.Bold)
}@Extend(Text)
export function body1() {.fontSize(16).fontColor('#333333')
}@Extend(Text)
export function body2() {.fontSize(14).fontColor('#666666')
}// 全局容器样式
@Styles
export function pageContainer() {.width('100%').height('100%').backgroundColor('#F5F5F5')
}@Styles
export function centerContainer() {.width('100%').height('100%').justifyContent(FlexAlign.Center).alignItems(ItemAlign.Center)
}// 全局卡片样式
@Styles
export function card() {.backgroundColor(Color.White).borderRadius(12).padding(16).margin({ left: 16, right: 16, top: 8, bottom: 8 })
}

使用全局样式:

import { h1, h2, body1, card } from './common/GlobalStyles'@Component
struct MyPage {build() {Column() {Text('大标题').h1()Text('副标题').h2()Text('正文内容').body1()Column() {Text('卡片内容')}.card()}}
}

4. 列表优化

4.1 List + ForEach(小数据量)

@Component
struct SimpleList {@State items: string[] = ['项目1', '项目2', '项目3']build() {List({ space: 10 }) {ForEach(this.items, (item: string, index: number) => {ListItem() {Row() {Text(`${index + 1}. ${item}`)}.padding(16).backgroundColor(Color.White).borderRadius(8)}}, (item: string) => item)  // key生成函数}.padding(16)}
}

4.2 LazyForEach(大数据量)

// 1. 实现数据源
class MyDataSource implements IDataSource {private dataArray: string[] = []private listeners: DataChangeListener[] = []constructor(data: string[]) {this.dataArray = data}// 数据总数totalCount(): number {return this.dataArray.length}// 获取指定索引的数据getData(index: number): string {return this.dataArray[index]}// 注册监听器registerDataChangeListener(listener: DataChangeListener): void {if (this.listeners.indexOf(listener) < 0) {this.listeners.push(listener)}}// 注销监听器unregisterDataChangeListener(listener: DataChangeListener): void {const pos = this.listeners.indexOf(listener)if (pos >= 0) {this.listeners.splice(pos, 1)}}// 添加数据public add(item: string): void {this.dataArray.push(item)this.notifyDataAdd(this.dataArray.length - 1)}// 通知数据添加notifyDataAdd(index: number): void {this.listeners.forEach(listener => {listener.onDataAdd(index)})}// 删除数据public remove(index: number): void {this.dataArray.splice(index, 1)this.notifyDataDelete(index)}// 通知数据删除notifyDataDelete(index: number): void {this.listeners.forEach(listener => {listener.onDataDelete(index)})}
}// 2. 使用LazyForEach
@Entry
@Component
struct LazyListDemo {private dataSource: MyDataSource = new MyDataSource([])aboutToAppear() {// 模拟大量数据for (let i = 0; i < 1000; i++) {this.dataSource.add(`项目 ${i + 1}`)}}build() {List({ space: 10 }) {LazyForEach(this.dataSource,(item: string, index: number) => {ListItem() {Row() {Text(`${index + 1}. ${item}`)}.padding(16).backgroundColor(Color.White).borderRadius(8)}},(item: string, index: number) => index.toString()  // key生成)}.padding(16).cachedCount(5)  // 缓存5个列表项}
}

4.3 下拉刷新 + 上拉加载

@Entry
@Component
struct RefreshLoadList {@State items: string[] = []@State isRefreshing: boolean = false@State isLoadingMore: boolean = false@State hasMore: boolean = trueprivate page: number = 1aboutToAppear() {this.loadData()}// 加载数据async loadData() {// 模拟网络请求setTimeout(() => {const newItems: string[] = []for (let i = 0; i < 20; i++) {newItems.push(`项目 ${(this.page - 1) * 20 + i + 1}`)}this.items = [...this.items, ...newItems]this.hasMore = this.page < 5  // 假设只有5页}, 1000)}// 下拉刷新async onRefresh() {this.isRefreshing = truethis.page = 1this.items = []await this.loadData()this.isRefreshing = false}// 上拉加载更多async onLoadMore() {if (this.isLoadingMore || !this.hasMore) {return}this.isLoadingMore = truethis.page++await this.loadData()this.isLoadingMore = false}build() {Column() {Refresh({ refreshing: $$this.isRefreshing }) {List({ space: 10 }) {ForEach(this.items, (item: string) => {ListItem() {Row() {Text(item)}.padding(16).backgroundColor(Color.White).borderRadius(8)}})// 加载更多提示if (this.hasMore) {ListItem() {Row() {if (this.isLoadingMore) {LoadingProgress().width(30).height(30)Text('加载中...')} else {Text('上拉加载更多')}}.justifyContent(FlexAlign.Center).padding(20)}} else {ListItem() {Text('没有更多了').textAlign(TextAlign.Center).padding(20)}}}.onReachEnd(() => {this.onLoadMore()})}.onRefreshing(() => {this.onRefresh()})}}
}

5. 表单处理

5.1 基础表单

@Entry
@Component
struct BasicForm {@State username: string = ''@State password: string = ''@State remember: boolean = falsehandleSubmit() {console.log('提交表单:', {username: this.username,password: this.password,remember: this.remember})}build() {Column({ space: 16 }) {Text('登录表单').fontSize(24).fontWeight(FontWeight.Bold)// 用户名输入Column({ space: 4 }) {Text('用户名').fontSize(14).fontColor('#666666')TextInput({ placeholder: '请输入用户名' }).onChange((value: string) => {this.username = value})}.alignItems(HorizontalAlign.Start)// 密码输入Column({ space: 4 }) {Text('密码').fontSize(14).fontColor('#666666')TextInput({ placeholder: '请输入密码' }).type(InputType.Password).onChange((value: string) => {this.password = value})}.alignItems(HorizontalAlign.Start)// 记住密码Row({ space: 8 }) {Checkbox().select(this.remember).onChange((value: boolean) => {this.remember = value})Text('记住密码')}// 提交按钮Button('登录').width('100%').onClick(() => {this.handleSubmit()})}.padding(24)}
}

5.2 表单验证

@Entry
@Component
struct ValidatedForm {@State username: string = ''@State password: string = ''@State usernameError: string = ''@State passwordError: string = ''// 验证用户名validateUsername(): boolean {if (this.username.length === 0) {this.usernameError = '用户名不能为空'return false}if (this.username.length < 3) {this.usernameError = '用户名至少3个字符'return false}this.usernameError = ''return true}// 验证密码validatePassword(): boolean {if (this.password.length === 0) {this.passwordError = '密码不能为空'return false}if (this.password.length < 6) {this.passwordError = '密码至少6个字符'return false}this.passwordError = ''return true}// 提交表单handleSubmit() {const isUsernameValid = this.validateUsername()const isPasswordValid = this.validatePassword()if (isUsernameValid && isPasswordValid) {console.log('表单验证通过,提交数据')// 执行提交逻辑}}build() {Column({ space: 16 }) {// 用户名输入Column({ space: 4 }) {Text('用户名')TextInput({ text: this.username }).onChange((value: string) => {this.username = valuethis.validateUsername()  // 实时验证})if (this.usernameError) {Text(this.usernameError).fontSize(12).fontColor(Color.Red)}}.alignItems(HorizontalAlign.Start)// 密码输入Column({ space: 4 }) {Text('密码')TextInput({ text: this.password }).type(InputType.Password).onChange((value: string) => {this.password = valuethis.validatePassword()  // 实时验证})if (this.passwordError) {Text(this.passwordError).fontSize(12).fontColor(Color.Red)}}.alignItems(HorizontalAlign.Start)Button('提交').width('100%').onClick(() => {this.handleSubmit()})}.padding(24)}
}

5.3 复杂表单(多字段)

// 定义表单数据接口
interface RegistrationForm {username: stringemail: stringphone: stringpassword: stringconfirmPassword: stringage: numbergender: stringacceptTerms: boolean
}// 定义验证错误接口
interface FormErrors {username?: stringemail?: stringphone?: stringpassword?: stringconfirmPassword?: string
}@Entry
@Component
struct RegistrationForm {@State formData: RegistrationForm = {username: '',email: '',phone: '',password: '',confirmPassword: '',age: 18,gender: '男',acceptTerms: false}@State errors: FormErrors = {}// 验证邮箱validateEmail(email: string): boolean {const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/return emailRegex.test(email)}// 验证手机号validatePhone(phone: string): boolean {const phoneRegex = /^1[3-9]\d{9}$/return phoneRegex.test(phone)}// 验证整个表单validateForm(): boolean {this.errors = {}if (!this.formData.username) {this.errors.username = '用户名不能为空'}if (!this.validateEmail(this.formData.email)) {this.errors.email = '邮箱格式不正确'}if (!this.validatePhone(this.formData.phone)) {this.errors.phone = '手机号格式不正确'}if (this.formData.password.length < 6) {this.errors.password = '密码至少6个字符'}if (this.formData.password !== this.formData.confirmPassword) {this.errors.confirmPassword = '两次密码不一致'}return Object.keys(this.errors).length === 0}// 提交表单handleSubmit() {if (this.validateForm()) {console.log('表单提交:', this.formData)// 执行提交逻辑}}// 表单项组件@BuilderFormItem(label: string, error?: string, content: () => void) {Column({ space: 4 }) {Text(label).fontSize(14).fontColor('#666666')content()if (error) {Text(error).fontSize(12).fontColor(Color.Red)}}.width('100%').alignItems(HorizontalAlign.Start)}build() {Scroll() {Column({ space: 16 }) {Text('用户注册').fontSize(24).fontWeight(FontWeight.Bold)this.FormItem('用户名', this.errors.username, () => {TextInput({ text: this.formData.username }).onChange((value: string) => {this.formData.username = value})})this.FormItem('邮箱', this.errors.email, () => {TextInput({ text: this.formData.email }).type(InputType.Email).onChange((value: string) => {this.formData.email = value})})this.FormItem('手机号', this.errors.phone, () => {TextInput({ text: this.formData.phone }).type(InputType.PhoneNumber).maxLength(11).onChange((value: string) => {this.formData.phone = value})})this.FormItem('密码', this.errors.password, () => {TextInput({ text: this.formData.password }).type(InputType.Password).onChange((value: string) => {this.formData.password = value})})this.FormItem('确认密码', this.errors.confirmPassword, () => {TextInput({ text: this.formData.confirmPassword }).type(InputType.Password).onChange((value: string) => {this.formData.confirmPassword = value})})// 性别选择Column({ space: 4 }) {Text('性别')Row({ space: 16 }) {Row({ space: 8 }) {Radio({ value: '男', group: 'gender' }).checked(this.formData.gender === '男').onChange((checked: boolean) => {if (checked) this.formData.gender = '男'})Text('男')}Row({ space: 8 }) {Radio({ value: '女', group: 'gender' }).checked(this.formData.gender === '女').onChange((checked: boolean) => {if (checked) this.formData.gender = '女'})Text('女')}}}.alignItems(HorizontalAlign.Start)// 同意条款Row({ space: 8 }) {Checkbox().select(this.formData.acceptTerms).onChange((value: boolean) => {this.formData.acceptTerms = value})Text('我已阅读并同意用户协议').fontSize(14)}Button('注册').width('100%').enabled(this.formData.acceptTerms).onClick(() => {this.handleSubmit()})}.padding(24)}}
}

6. 动画效果

6.1 显式动画

@Entry
@Component
struct ExplicitAnimation {@State scaleValue: number = 1@State rotateAngle: number = 0@State opacity: number = 1build() {Column({ space: 20 }) {// 动画目标Image($r('app.media.icon')).width(100).height(100).scale({ x: this.scaleValue, y: this.scaleValue }).rotate({ angle: this.rotateAngle }).opacity(this.opacity)// 控制按钮Button('缩放动画').onClick(() => {animateTo({duration: 1000,  // 持续时间curve: Curve.EaseInOut,  // 动画曲线onFinish: () => {console.log('动画完成')}}, () => {this.scaleValue = this.scaleValue === 1 ? 1.5 : 1})})Button('旋转动画').onClick(() => {animateTo({ duration: 1000 }, () => {this.rotateAngle = this.rotateAngle + 360})})Button('淡入淡出').onClick(() => {animateTo({ duration: 500 }, () => {this.opacity = this.opacity === 1 ? 0.3 : 1})})}.width('100%').height('100%').justifyContent(FlexAlign.Center)}
}

6.2 组件内置动画

@Entry
@Component
struct ComponentAnimation {@State isExpanded: boolean = falsebuild() {Column({ space: 16 }) {Text('点击展开/收起').fontSize(20).onClick(() => {this.isExpanded = !this.isExpanded})Column() {Text('这是展开的内容').fontSize(16).padding(16)}.width('100%').height(this.isExpanded ? 200 : 0).backgroundColor('#F0F0F0').animation({duration: 300,curve: Curve.EaseInOut})}.padding(24)}
}

6.3 转场动画

@Entry
@Component
struct TransitionAnimation {@State isShow: boolean = truebuild() {Column({ space: 20 }) {Button(this.isShow ? '隐藏' : '显示').onClick(() => {animateTo({ duration: 500 }, () => {this.isShow = !this.isShow})})if (this.isShow) {Column() {Text('这是一个卡片').fontSize(18)}.width('80%').height(200).backgroundColor('#4D91FF').borderRadius(12).transition({type: TransitionType.Insert,opacity: 0,translate: { x: 0, y: 100 },scale: { x: 0.5, y: 0.5 }}).transition({type: TransitionType.Delete,opacity: 0,translate: { x: 0, y: -100 },scale: { x: 1.5, y: 1.5 }})}}.width('100%').height('100%').justifyContent(FlexAlign.Center)}
}

7. 资源管理

7.1 资源引用方式

@Component
struct ResourceDemo {build() {Column() {// 1. 引用媒体资源Image($r('app.media.icon_home'))Image($r('app.media.bg_splash'))// 2. 引用字符串资源Text($r('app.string.app_name'))Text($r('app.string.welcome_message'))// 3. 引用颜色资源Text('标题').fontColor($r('app.color.primary_color')).backgroundColor($r('app.color.background_color'))// 4. 引用尺寸资源Text('内容').fontSize($r('app.float.title_font_size')).padding($r('app.float.global_padding'))// 5. 引用布尔资源Button('按钮').enabled($r('app.boolean.button_enabled'))}}
}

7.2 多语言支持

resources/zh_CN/element/string.json:

{"string": [{"name": "app_name","value": "泰科健康"},{"name": "welcome","value": "欢迎使用"},{"name": "home_tab","value": "首页"}]
}

resources/en_US/element/string.json:

{"string": [{"name": "app_name","value": "HealthyMate"},{"name": "welcome","value": "Welcome"},{"name": "home_tab","value": "Home"}]
}

使用:

Text($r('app.string.welcome'))
// 系统语言为中文时显示:欢迎使用
// 系统语言为英文时显示:Welcome

7.3 主题切换

// 定义主题接口
interface Theme {primaryColor: stringbackgroundColor: stringtextColor: stringcardColor: string
}// 浅色主题
const lightTheme: Theme = {primaryColor: '#4D91FF',backgroundColor: '#F5F5F5',textColor: '#333333',cardColor: '#FFFFFF'
}// 深色主题
const darkTheme: Theme = {primaryColor: '#5FA3FF',backgroundColor: '#1A1A1A',textColor: '#EEEEEE',cardColor: '#2A2A2A'
}// 存储当前主题
AppStorage.setOrCreate('currentTheme', lightTheme)@Entry
@Component
struct ThemeDemo {@StorageLink('currentTheme') theme: Theme = lightThemeswitchTheme() {const newTheme = this.theme === lightTheme ? darkTheme : lightThemeAppStorage.set('currentTheme', newTheme)}build() {Column({ space: 16 }) {Text('主题演示').fontSize(24).fontColor(this.theme.textColor)Column() {Text('这是卡片内容')}.padding(16).backgroundColor(this.theme.cardColor).borderRadius(12)Button('切换主题').backgroundColor(this.theme.primaryColor).onClick(() => {this.switchTheme()})}.width('100%').height('100%').backgroundColor(this.theme.backgroundColor).padding(24)}
}

8. 调试技巧

8.1 日志输出

// 基础日志
console.log('普通日志')
console.info('信息日志')
console.warn('警告日志')
console.error('错误日志')// 带标签的日志
console.log('[HomeComp]', '页面加载完成')
console.error('[API]', '请求失败:', error)// 输出对象
const user = { name: '张三', age: 25 }
console.log('用户信息:', JSON.stringify(user))

8.2 断点调试

@Component
struct DebugDemo {@State count: number = 0handleClick() {// 在这里设置断点console.log('点击前:', this.count)this.count++console.log('点击后:', this.count)}build() {Button(`计数: ${this.count}`).onClick(() => {this.handleClick()})}
}

8.3 性能监控

@Component
struct PerformanceDemo {@State items: number[] = []aboutToAppear() {const startTime = Date.now()// 模拟耗时操作for (let i = 0; i < 10000; i++) {this.items.push(i)}const endTime = Date.now()console.log(`数据加载耗时: ${endTime - startTime}ms`)}build() {List() {ForEach(this.items, (item: number) => {ListItem() {Text(item.toString())}})}}
}

9. 项目实战案例

9.1 完整的登录页面

import { router } from '@kit.ArkUI'@Entry
@Component
struct CompleteLoginPage {@State username: string = ''@State password: string = ''@State usernameError: string = ''@State passwordError: string = ''@State isLoading: boolean = false@State rememberPassword: boolean = false// 验证表单validateForm(): boolean {this.usernameError = ''this.passwordError = ''if (!this.username) {this.usernameError = '请输入用户名'return false}if (!this.password) {this.passwordError = '请输入密码'return false}if (this.password.length < 6) {this.passwordError = '密码至少6个字符'return false}return true}// 登录async handleLogin() {if (!this.validateForm()) {return}this.isLoading = truetry {// 模拟API请求await new Promise((resolve) => setTimeout(resolve, 2000))// 保存登录状态AppStorage.set('isLoggedIn', true)AppStorage.set('username', this.username)// 跳转到首页router.replaceUrl({ url: 'pages/Index' })} catch (error) {console.error('登录失败:', error)// 显示错误提示} finally {this.isLoading = false}}build() {Column() {// Logo区域Column({ space: 12 }) {Image($r("app.media.icon_app")).width(100).height(100)Text('泰科健康').fontSize(28).fontWeight(FontWeight.Bold)Text('您的健康管理专家').fontSize(14).fontColor('#999999')}.margin({ top: 60, bottom: 40 })// 表单区域Column({ space: 20 }) {// 用户名输入Column({ space: 4 }) {TextInput({ placeholder: '请输入用户名' }).onChange((value: string) => {this.username = valuethis.usernameError = ''}).borderRadius(8).padding({ left: 16, right: 16, top: 12, bottom: 12 })if (this.usernameError) {Text(this.usernameError).fontSize(12).fontColor(Color.Red).width('100%')}}// 密码输入Column({ space: 4 }) {TextInput({ placeholder: '请输入密码' }).type(InputType.Password).onChange((value: string) => {this.password = valuethis.passwordError = ''}).borderRadius(8).padding({ left: 16, right: 16, top: 12, bottom: 12 })if (this.passwordError) {Text(this.passwordError).fontSize(12).fontColor(Color.Red).width('100%')}}// 记住密码 & 忘记密码Row() {Row({ space: 8 }) {Checkbox().select(this.rememberPassword).onChange((value: boolean) => {this.rememberPassword = value})Text('记住密码').fontSize(14)}Text('忘记密码').fontSize(14).fontColor('#4D91FF').onClick(() => {// 跳转到忘记密码页面})}.width('100%').justifyContent(FlexAlign.SpaceBetween)// 登录按钮Button() {if (this.isLoading) {LoadingProgress().width(20).height(20).color(Color.White)} else {Text('登录').fontSize(16).fontColor(Color.White)}}.width('100%').height(48).borderRadius(24).backgroundColor('#4D91FF').enabled(!this.isLoading).onClick(() => {this.handleLogin()})// 注册提示Row({ space: 4 }) {Text('还没有账号?').fontSize(14).fontColor('#666666')Text('立即注册').fontSize(14).fontColor('#4D91FF').onClick(() => {router.pushUrl({ url: 'pages/RegisterPage' })})}// 其他登录方式Column({ space: 16 }) {Row() {Divider().strokeWidth(1).color('#E0E0E0').layoutWeight(1)Text('其他登录方式').fontSize(12).fontColor('#999999').padding({ left: 16, right: 16 })Divider().strokeWidth(1).color('#E0E0E0').layoutWeight(1)}.margin({ top: 20 })Row({ space: 32 }) {Image($r('app.media.icon_wechat')).width(40).height(40).onClick(() => {// 微信登录})Image($r('app.media.icon_qq')).width(40).height(40).onClick(() => {// QQ登录})Image($r('app.media.icon_weibo')).width(40).height(40).onClick(() => {// 微博登录})}}}.width('88%')}.width('100%').height('100%').backgroundColor('#F5F5F5')}
}

9.2 带搜索的列表页面

@Entry
@Component
struct SearchListPage {@State searchText: string = ''@State allItems: string[] = []@State filteredItems: string[] = []@State selectedCategory: string = '全部'private categories: string[] = ['全部', '健康', '医疗', '运动']aboutToAppear() {// 初始化数据this.allItems = ['血压监测', '心率检测', '血糖跟踪','运动计步', '睡眠监测', '健康饮食','医院排行', '附近医院', '检查解读']this.filteredItems = [...this.allItems]}// 搜索过滤handleSearch(text: string) {this.searchText = textthis.filterItems()}// 分类过滤handleCategoryChange(category: string) {this.selectedCategory = categorythis.filterItems()}// 综合过滤filterItems() {let filtered = [...this.allItems]// 搜索文本过滤if (this.searchText) {filtered = filtered.filter(item =>item.includes(this.searchText))}// 分类过滤(示例)if (this.selectedCategory !== '全部') {// 实际项目中根据分类筛选}this.filteredItems = filtered}build() {Column() {// 搜索栏Row() {Search({ placeholder: '搜索健康内容' }).onChange((value: string) => {this.handleSearch(value)})}.padding(16).backgroundColor(Color.White)// 分类标签Scroll() {Row({ space: 12 }) {ForEach(this.categories, (category: string) => {Text(category).fontSize(14).padding({ left: 16, right: 16, top: 8, bottom: 8 }).borderRadius(16).backgroundColor(this.selectedCategory === category ? '#4D91FF' : '#F0F0F0').fontColor(this.selectedCategory === category ? Color.White : '#333333').onClick(() => {this.handleCategoryChange(category)})})}}.scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off).padding({ left: 16, right: 16, top: 12, bottom: 12 })Divider()// 搜索结果列表if (this.filteredItems.length > 0) {List({ space: 10 }) {ForEach(this.filteredItems, (item: string) => {ListItem() {Row() {Text(item).fontSize(16)}.width('100%').padding(16).backgroundColor(Color.White).borderRadius(8)}})}.padding(16).layoutWeight(1)} else {// 空状态Column({ space: 16 }) {Image($r('app.media.icon_empty')).width(120).height(120)Text('暂无搜索结果').fontSize(16).fontColor('#999999')}.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)}}.width('100%').height('100%').backgroundColor('#F5F5F5')}
}

总结

这份实战技巧文档涵盖了:

  1. 组件通信 - 父子、兄弟、跨组件通信
  2. 自定义构建 - @Builder、@BuilderParam 高级用法
  3. 样式管理 - @Extend、@Styles、全局样式
  4. 列表优化 - LazyForEach、下拉刷新、上拉加载
  5. 表单处理 - 验证、复杂表单、实时反馈
  6. 动画效果 - 显式动画、转场、组件动画
  7. 资源管理 - 多语言、主题切换
  8. 调试技巧 - 日志、断点、性能监控
  9. 实战案例 - 完整的登录页、搜索列表页

建议配合《HarmonyNext学习指南-完整版.md》一起阅读,从基础到进阶,全面掌握 HarmonyOS Next 开发!🎉

http://www.dtcms.com/a/490456.html

相关文章:

  • 【鸿蒙进阶-7】鸿蒙与web混合开发
  • HarmonyOS Next 快速参考手册
  • 8.list的模拟实现
  • 鸿蒙NEXT按键拦截与监听开发指南
  • 网站建设等级定级企查查官网查企业网页版
  • 【数据结构】基于Floyd算法的最短路径求解
  • 【传感器技术】入门红外传感器技术
  • 成都哪里做网站便宜郴州新网招聘官网
  • 天地一体:卫星互联网与5G/6G的融合之路
  • BCH码编译码仿真与误码率性能分析
  • 5G+AIoT智赋,AI电力加密边缘网关智慧电网数字化运维解决方案
  • 深度学习:PyTorch Lightning,训练流程标准化?
  • 100G 单纤光模块:高带宽传输新选择,选型与应用全解析
  • 网站开发的技术有gis网站开发实战教程
  • 汕头网站建设技术外包模板网站怎么用
  • 2025-10-16-TH 开源框架JeecgBoot Pro搭建流程
  • 二叉树搜索树插入,查找,删除,Key/Value二叉搜索树场景应用+源码实现
  • 2025年10月版集成RagFlow和Dify的医疗知识库自动化查询(数据篇)
  • UVa 12803 Arithmetic Expressions
  • json转excel xlsx文件
  • 【C++】深入理解string类(5)
  • 六、Hive的基本使用
  • 铜陵网站建设推广江苏核酸检测机构
  • 电子商务网站建设含义如果做车站车次查询的网站需要什么消息信息
  • 【JETSON+FPGA+GMSL】实测分享 | 如何实现激光雷达与摄像头高精度时间同步?
  • 建网站权威公司dw怎么做打开网站跳出提示
  • 阅读:REACT: SYNERGIZING REASONING AND ACTING INLANGUAGE MODELS(在语言模型中协同推理与行动)
  • 语义三角论对AI自然语言处理中深层语义分析的影响与启示
  • SpringBoot 启动时执行某些操作的 8 种方式
  • Cloud IDE vs 本地IDE:AI编程时代的“降维打击“