到底是 TS 的远房亲戚,还是 UI 的狠角色?”——一口气讲透 ArkTS 语法、范式与响应式脑回路!

举报
喵手 发表于 2025/10/31 17:22:47 2025/10/31
【摘要】 开篇语哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,...

开篇语

哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛

  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。

  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!

前言

说真的,第一次写 ArkTS,我的心理活动是:啊这,不就是 TypeScript 换了件新外套吗?结果一写 UI,状态像猫一样说变就变,@State、@Prop、@Link、@Provide/@Consume 在屏幕上跳康康舞👀。ArkTS并不是把 TS 搬到端侧那么简单,它把UI 声明式 + 响应式状态系统揉进语言/框架语义里,再配上资源系统、编译期装饰器和端侧约束,做出来的是“像 TS,又不止 TS”的开发体验。下面我们从语言特性 → 与 TypeScript 的差异 → 函数式与响应式编程模式一路梳理,顺手给到可跑的代码片段与避坑心法。

目录

  1. 为什么 ArkTS 不是“换皮 TS”
  2. ArkTS 语法特性,一屏看懂
  3. 与 TypeScript 的异同(别被语法糖迷了眼)
  4. 函数式与响应式编程:在 ArkUI 里跑得顺才叫真本事
  5. 进阶实战:三段代码掌握状态共享、派生状态与副作用
  6. 性能与工程化心法:别在 build() 里挖坑
  7. 小结与加餐:常见踩坑清单

1) 为什么 ArkTS 不是“换皮 TS”

核心观点:ArkTS 面向端侧 UI 与应用框架(ArkUI/ArkCompiler),把状态与界面的映射做成“一等公民”。这意味着:

  • UI = 函数(状态) 的理念被语言与装饰器加持,调用链天然响应式;
  • 编译器参与更深:装饰器、资源、路由、生命周期能在编译阶段“看见并优化”;
  • 运行时约束更严格:端侧环境为了性能与安全,对动态特性更保守(例如不鼓励 eval、反射滥用)。

一句话:用 TS 的手感,写“端上更像框架内核语言”的东西


2) ArkTS 语法特性,一屏看懂

2.1 组件与装饰器:@Entry@Component@State

// xxx.ets
@Entry
@Component
struct CounterPage {
  @State count: number = 0    // 本地可变状态
  @State step: number = 1

  // 生命周期(常用片段)
  aboutToAppear() {
    // 页面可见前:读取缓存、拉配置
  }

  build() {
    Column({ space: 16 }) {
      Text(`Count: ${this.count}`).fontSize(22).fontWeight(600)
      Row({ space: 12 }) {
        Button(`+${this.step}`).onClick(() => this.count += this.step)
        Button('Reset').type(ButtonType.Normal).onClick(() => {
          this.count = 0
        })
      }
      Slider({ value: this.step, min: 1, max: 10 })
        .onChange((v: number) => this.step = Math.floor(v))
    }
    .padding(20)
  }
}

读法build() 里就是声明式 UI,视图树由 Column/Row/Text/Button 等函数语义化描述;任何 @State 变化都会触发精准重渲染(差量更新)。

2.2 跨组件状态传递:@Prop / @Link / @Provide / @Consume

  • @Prop:父传子只读快照;
  • @Link:父子共享同一状态引用(双向联动);
  • @Provide / @Consume类似依赖注入,上层提供,下层任何后代都能消费,不需要层层传参。
// 父
@Entry
@Component
struct App {
  @State theme: 'light'|'dark' = 'light'
  @Provide('themeKey') providedTheme: string = this.theme

  build() {
    Column() {
      ThemeToggle({ })
      ContentArea()
    }
  }
}

@Component
struct ThemeToggle {
  @Consume('themeKey') theme!: string
  build() {
    Row() {
      Text(`Theme: ${this.theme}`)
      Button('Switch').onClick(() => {
        // 直接改 consume 的来源(共享引用)
        this.theme = this.theme === 'light' ? 'dark' : 'light'
      })
    }
  }
}

2.3 资源系统与单位:$r('app.string.xxx')vp/fp/px

  • 统一访问字符串/颜色/尺寸/图片等资源:$r('app.string.hello')
  • UI 单位:vp(与屏幕密度无关的视觉像素)、fp(字体像素)、px(物理像素)。
    心法:布局用 vp,文字用 fp,像素级控制再谈 px

2.4 异步与任务:async/await、定时、轻量任务

ArkTS 支持 Promise/async/await。端侧 I/O、动画、定时器都建议避开阻塞,在生命周期钩子或事件回调里异步化处理


3) 与 TypeScript 的异同(别被语法糖迷了眼)

维度 TypeScript ArkTS
语言定位 JS 的类型超集,面向广义前后端 TS 手感 + 端侧 UI/状态语义,与 ArkUI、编译器深度耦合
组件模型 React/Vue/Svelte 等库提供 语言级装饰器 + 框架一体化@Entry/@Component/@State 等)
状态系统 各框架自带(React Hooks、Vue 响应式等) 官方内建@State/@Link/@Provide/@Consume/@Prop、精细化渲染
资源/样式 由框架/构建工具约定 内建资源系统 + 单位体系(vp/fp/px)
运行时能力 浏览器/Node,自由度高 端侧约束更严,更强调可预测与性能(谨慎动态特性)
编译与优化 tsc + bundler + 框架优化 ArkCompiler 参与更深,装饰器/路由/资源可被分析与优化
API 生态 npm 世界 HarmonyOS 能力集成(权限、硬件、系统服务)

易混点

  • class/struct 语义:ArkTS 组件常见 struct … build() 书写范式(也可见到 class 风格 API),以组件生命周期与状态为中心
  • 装饰器并非任意 TS 装饰器能无缝复用;ArkTS 的装饰器有特定编译期含义。
  • 不要滥用 any/反射:端侧更希望静态可分析、可裁剪。

4) 函数式与响应式编程:在 ArkUI 里跑得顺才叫真本事

4.1 函数式味道:数据不可变、纯函数组件化

  • 纯函数思想:给定状态→渲染确定 UI;
  • 不可变更新:状态更新倾向新对象(帮助脏检查与最小重绘);
  • 高阶函数map/filter/reduce 处理列表渲染更稳。
@Component
struct TodoList {
  @State todos: { id:number; title:string; done:boolean }[] = []

  toggle(id: number) {
    // 不可变更新:创建新数组
    this.todos = this.todos.map(t => t.id === id ? { ...t, done: !t.done } : t)
  }

  build() {
    List() {
      ForEach(this.todos, (t) => {
        ListItem() {
          Row({ space: 12 }) {
            Checkbox({ checked: t.done }).onChange(() => this.toggle(t.id))
            Text(t.title).decoration(t.done ? {type: TextDecorationType.LineThrough} : {})
          }
        }
      })
    }
  }
}

4.2 响应式内核:状态图精确更新

  • 任一 @State/@Link 数值变化 → 框架计算受影响的视图子树局部重渲染
  • 派生状态建议用计算函数或 @Watch 观察,避免在 build() 里做重逻辑
@Component
struct PricePanel {
  @State count: number = 1
  @State unit: number = 12.5
  total(): number { return Number((this.count * this.unit).toFixed(2)) }

  @Watch('count')
  onCountChanged() {
    // 轻量副作用:例如打点、缓存
  }

  build() {
    Column({ space: 8 }) {
      Text(`Unit: $${this.unit}`)
      Text(`Count: ${this.count}`)
      Text(`Total: $${this.total()}`).fontWeight(700)
      Slider({ value: this.count, min: 1, max: 10 }).onChange(v => this.count = Math.floor(v))
    }.padding(16)
  }
}

口诀:数据改,UI动;副作用,轻松控;重计算,别放 build


5) 进阶实战:三段代码掌握状态共享、派生状态与副作用

5.1 全局主题与本地覆盖(@Provide/@Consume + 局部状态)

@Entry
@Component
struct Root {
  @State theme: 'light' | 'dark' = 'light'
  @Provide('theme') appTheme: string = this.theme

  build() {
    Column({ space: 12 }) {
      Button('Toggle Theme').onClick(() => {
        this.theme = this.theme === 'light' ? 'dark' : 'light'
      })
      CardArea()
      FooterBar()
    }.padding(16)
  }
}

@Component
struct CardArea {
  @Consume('theme') theme!: string
  @State localHighContrast: boolean = false

  build() {
    Column({ space: 8 }) {
      Row({ space: 8 }) {
        Text(`Theme from root: ${this.theme}`)
        Switch({ checked: this.localHighContrast })
          .onChange((on) => this.localHighContrast = on)
      }
      // 局部覆盖样式
      Card().backgroundColor(this.localHighContrast ? '#000' : (this.theme === 'dark' ? '#222' : '#fff'))
      .width('100%').height(80)
    }
  }
}

5.2 列表派生与 memo 化(避免无谓渲染)

@Component
struct SearchableList {
  @State keyword: string = ''
  @State items: string[] = ['ArkTS', 'TypeScript', 'ArkUI', 'Harmony', 'State', 'Reactive']

  // 简易 memo:输入相同就重用
  private _cache: { k: string; out: string[] } | null = null
  filtered(): string[] {
    if (!this._cache || this._cache.k !== this.keyword) {
      const out = this.items.filter(x => x.toLowerCase().includes(this.keyword.toLowerCase()))
      this._cache = { k: this.keyword, out }
    }
    return this._cache.out
  }

  build() {
    Column({ space: 10 }) {
      TextInput({ placeholder: 'Type to search…' })
        .onChange((v: string) => this.keyword = v)
      List() {
        ForEach(this.filtered(), (name) => ListItem() { Text(name) })
      }.cachedCount(20) // 大列表缓存优化
    }.padding(16)
  }
}

5.3 副作用与异步串联(生命周期 + 取消)

@Component
struct RemotePanel {
  @State loading: boolean = false
  @State data: { title: string; body: string } | null = null
  private cancelled = false

  aboutToAppear() {
    this.fetchData()
  }

  aboutToDisappear() {
    // 标记取消,避免卸载后 setState
    this.cancelled = true
  }

  async fetchData() {
    this.loading = true
    try {
      const res = await fetchSomehow('/api/detail') // 替换为端侧请求能力
      if (!this.cancelled) this.data = res
    } finally {
      if (!this.cancelled) this.loading = false
    }
  }

  build() {
    if (this.loading) {
      return Column() { LoadingProgress() }.height('100%').justifyContent(FlexAlign.Center)
    }
    if (!this.data) {
      return Column() { Text('No data') }.height('100%').justifyContent(FlexAlign.Center)
    }
    return Column({ space: 8 }) {
      Text(this.data.title).fontSize(20).fontWeight(700)
      Text(this.data.body).fontSize(16)
    }.padding(16)
  }
}

6) 性能与工程化心法:别在 build() 里挖坑

  1. build() 只做“拼界面”:重计算/请求/写缓存都放到生命周期或事件回调。
  2. 最小化状态:把能算出来的用派生函数算,不要塞进 @State
  3. 不可变更新:数组/对象建议返回新引用,帮助框架判断差异。
  4. 列表优化ForEach 配合稳定 key / cachedCount;分页/瀑布流尽量增量渲染。
  5. 分层状态全局用 @Provide,跨层 @Consume,兄弟用 @Link or 事件,避免大状态洪水。
  6. 副作用可取消:涉及异步的页面,记得在 aboutToDisappear 做取消标记。
  7. 资源与单位:布局用 vp,文字 fp,矢量/九宫优先,图片按 devicePixelRatio 管理。
  8. 严格类型:端侧更需要“可分析”,少用 any,尽量定死模型类型与接口契约。

7) 小结与加餐:常见踩坑清单

  • 状态循环更新@Watch 里又改回监听的 @State,形成死循环?→ 条件判断 + 节流。
  • 误把 @Prop 当可写@Prop 是只读快照,双向联动用 @Link
  • 把重逻辑塞进 build():页面抖动、掉帧,移动端更敏感。
  • 滥用全局状态@Provide 别成“全局黑洞”,要有边界与层级。
  • 动态特性过多eval/反射 等不利于编译优化与裁剪,端侧慎用。
  • 资源直写常量:跨主题/多语言时全改一遍很痛,统一 $r()
  • 长列表刷新:没做缓存与 key,滚一滚 CPU 就给你“上香”。

TL;DR(一句话)

ArkTS = TS 的类型手感 + 端侧声明式 UI + 状态为王的响应式内核。写好它的秘诀是:最小状态、不可变更新、把副作用关进笼子。当你把“数据 → 视图”这条链打磨顺滑,ArkTS 就会像台油门线绷紧的摩托,轻一点就窜起来,爽!🏍️💨


参考练习任务(自行扩展)

  1. TodoList 改成分组与搜索并做分页懒加载
  2. RemotePanel错误重试 + 指数退避,并在 aboutToDisappear 取消未完成请求。
  3. @Provide/@Consume多主题(浅色/深色/高对比),并抽出样式函数

… …

文末

好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。

… …

学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!

wished for you successed !!!


⭐️若喜欢我,就请关注我叭。

⭐️若对您有用,就请点赞叭。
⭐️若有疑问,就请评论留言告诉我叭。


版权声明:本文由作者原创,转载请注明出处,谢谢支持!

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。