还在纠结 eTS、JS、ArkTS?不如三小时吃透“ArkUI × ArkTS”双栈,香不香?

🏆本文收录于「滚雪球学SpringBoot」专栏(全网一个名),手把手带你零基础入门Spring Boot,从入门到就业,助你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
环境说明:Windows 10 + IntelliJ IDEA 2021.3.2 + Jdk 1.8
前言
先抛一个灵魂拷问:你是要“能跑的 Demo”,还是“敢上线、能扩展、可观测”的鸿蒙应用?如果你也在 ArkUI 与 ArkTS 双栈、eTS 与 JS 应用的岔路口踟蹰,那这篇就是为你准备的“人话+实例+避坑指南”。我不跟你虚的:概念对齐 -> 对比清单 -> 代码示例 -> 实战范式,一口气把四个主题讲顺——“看得懂”“抄得走”“改得快”。走起!🚀
1) 双栈开发模式到底“双”在哪?
所谓“ArkUI × ArkTS 双栈”,我更喜欢拆成两层来理解:
- UI 栈(ArkUI):声明式组件体系(
Text、Row、Column、List…)+ 生命周期/路由/过渡/动画。 - 语言栈(ArkTS / eTS / JS):同样写 ArkUI,但用的语言层能力不同——类型系统、装饰器、编译优化 差异会直接影响可维护性与性能潜力。
再进一步,在一些业务里你还会引入:
- 能力栈(C/C++/Rust 通过 NAPI/NDK):做算法/多媒体/加解密等“重活”,上层 ArkTS/JS 调用即可。
一句话:ArkUI 统一 UI 编程范式,ArkTS(eTS)与 JS 决定你写代码的“刀锋锐利度”;必要时用原生扩展把性能拉满。
2) eTS 与 JS 应用:同门兄弟,有何取舍?
注:很多资料把 eTS(extended TS,历史叫法)与 ArkTS 混着提。当前推荐主力是 ArkTS(面向 ArkUI 的 TypeScript 超集),JS 一样可用,但工程体验与类型安全度不同。
| 维度 | eTS / ArkTS 应用 | JS 应用 |
|---|---|---|
| 语言特性 | TS 超集 + 装饰器(@State/@Provide/...)+ 更强类型推导 |
纯 JS,灵活但类型安全不足 |
| 组件声明 | @Component/@Entry + build(),强约束 |
函数式/对象式组件,约束较弱 |
| 状态管理 | @State/@Prop/@Link/@Provide/@Consume、AppStorage、@Observed |
以普通对象/闭包为主,需人为约束 |
| 编译与优化 | 编译期做更多语义检查与优化,体积/性能更可控 | 动态特性强,优化空间受限 |
| 团队协作 | 类型即契约,回归 IDE 驱动开发 | 自由度高,易“各写各的” |
| 适用场景 | 中大型、多人协作、长期维护 | 轻量 Demo、原型、对 TS 抗拒的场景 |
实话实说:如果不是特殊历史包袱,优先选 ArkTS(eTS 语法谱系)。写起来更自洽,调试、联调、Review 都更省心。
3) ArkTS 语法特性与组件绑定:装饰器、数据流与编译期校验
ArkTS 面向 ArkUI 的“声明式 + 响应式”语法糖,关键在 组件装饰器 与 状态装饰器。下面这张“口袋卡”够你对拍源码了👇
3.1 组件与生命周期
@Entry
@Component
struct HomePage {
aboutToAppear() { /* 首次渲染前 */ }
aboutToDisappear() { /* 销毁前 */ }
build() {
Column() {
Text('Hello ArkTS')
}.padding(16)
}
}
@Entry:应用或路由的入口组件@Component:声明式组件,必须实现build()- 生命周期:
aboutToAppear/…与路由过渡相呼应
3.2 数据与绑定(“单向为主、双向慎用”)
| 目标 | 用啥 | 绑定说明 |
|---|---|---|
| 组件内部可变状态 | @State |
改即刷新,范围局部 |
| 父传子只读 | @Prop |
子不可直接改 |
| 父子双向 | @Link |
少用在深层树,易乱 |
| 跨层共享 | @Provide/@Consume |
“祖孙通信”,像依赖注入 |
| 全局共享/持久 | AppStorage/@StorageLink |
可作为全局会话/主题 |
| 可观察对象 | @Observed/@ObjectLink |
对象属性级追踪 |
示例:父传子 + 双向回写
@Entry
@Component
struct App {
@State name: string = 'ArkUI'
build() {
Column() {
ProfileEditor({ title: 'Nickname' , text: this.name })
.onSaved((v: string) => this.name = v)
Text(`Hi, ${this.name}!`).fontSize(20)
}.padding(20)
}
}
@Component
struct ProfileEditor {
@Prop title: string
@Link text: string // 与父同步(慎用深层)
private onSavedCb?: (v: string) => void
onSaved(cb: (v: string) => void) { this.onSavedCb = cb; return this }
build() {
Column({ space: 8 }) {
Text(this.title).fontWeight(FontWeight.Bold)
TextInput({ text: this.text }).onChange(v => this.text = v)
Button('Save').onClick(() => this.onSavedCb?.(this.text))
}
}
}
经验:能用单向就单向(
@Prop+ 事件回调);@Link只在同域小组件里使用,避免“状态四处窜”。
3.3 样式与链式修饰
ArkUI 支持“语义组件 + 链式样式”:
Text('ArkUI × ArkTS')
.fontSize(22)
.fontWeight(FontWeight.Medium)
.fontColor('#222')
.padding({ top: 12, bottom: 8 })
优点:读起来像句子,且对 IDE 友好(补全 + 校验)。
4) 响应式 UI 编程模式:状态 → 视图 → 动画的“顺拐”节奏
4.1 隐式为主:让“样式变化”自动过渡
@State progress: number = 0
Rectangle()
.width(this.progress + '%')
.height(6)
.borderRadius(3)
.animation({ duration: 300, curve: Curve.EaseInOut })
- 只改状态,样式自动“丝滑”过渡
- 列表/表单/微交互的默认首选
4.2 显式兜底:需要编排就上 animateTo
@State open: boolean = false
@State opacity: number = 0
@State offsetY: number = -16
Button(this.open ? 'Hide' : 'Show')
.onClick(() => {
animateTo({ duration: 260, curve: Curve.Spring }, () => {
this.open = !this.open
this.opacity = this.open ? 1 : 0
this.offsetY = this.open ? 0 : -16
})
})
Column()
.opacity(this.opacity)
.translate({ y: this.offsetY })
- 时序可控、联动强,但请集中管理,别层层嵌套
4.3 过渡(Transition):让组件“进场/退场”有礼貌
ListItem() {
/* ... */
}
.transition(
TransitionEffect.OPACITY
.combine(TransitionEffect.SCALE)
.animation({ duration: 220, curve: Curve.Spring })
)
口诀:数据单向流 → 视图声明式 → 动画最少必要。先稳,再漂亮。
5) 可运行的小示例:从 JS 版到 ArkTS 版的“同题双解”
目标:搜索 + 收藏的微型列表。感受 JS 与 ArkTS 在“类型/装饰器/可维护性”上的差异。
5.1 JS 版本(轻量但松散)
//@ts-nocheck // 这里故意不校验以示对比
@Entry
@Component
struct SearchPage {
build() {
this.keyword = ''
this.favs = []
Column({ space: 10 }) {
TextInput({ placeholder: 'Search...' })
.onChange(v => this.keyword = v)
List() {
ForEach(mockData.filter(i => i.includes(this.keyword)), (item) => {
ListItem() {
Row({ space: 8 }) {
Text(item)
Blank()
Button('Fav').onClick(() => {
if (!this.favs.includes(item)) this.favs.push(item)
})
}
}
})
}
Text('Favorites: ' + JSON.stringify(this.favs))
}.padding(16)
}
}
- 优点:写得快
- 缺点:无类型护栏、状态来源松散、复杂后易乱
5.2 ArkTS 版本(强约束、好维护)
type Item = { id: number; name: string }
const DATA: Item[] = [
{ id: 1, name: 'ArkUI' },
{ id: 2, name: 'ArkTS' },
{ id: 3, name: 'eTS' },
{ id: 4, name: 'JS App' },
]
@Entry
@Component
struct SearchPage {
@State keyword: string = ''
@State favs: Set<number> = new Set()
build() {
Column({ space: 12 }) {
Text('Search & Favorite').fontSize(20).fontWeight(FontWeight.Bold)
TextInput({ placeholder: 'Type to filter...' })
.onChange(v => this.keyword = v)
List() {
ForEach(this.filtered(DATA), (item: Item) => {
ListItem() {
Row({ space: 8 }) {
Text(item.name)
Blank()
Button(this.favs.has(item.id) ? '★' : '☆')
.onClick(() => this.toggle(item.id))
}
.padding(8)
}
.transition(TransitionEffect.OPACITY.animation({ duration: 180 }))
})
}
Text(`Favorites: ${[...this.favs].join(', ') || 'None'}`)
.fontColor('#666')
}
.padding(16)
}
private filtered(src: Item[]): Item[] {
const k = this.keyword.trim().toLowerCase()
if (!k) return src
return src.filter(x => x.name.toLowerCase().includes(k))
}
private toggle(id: number) {
// 集中式修改,易测
if (this.favs.has(id)) this.favs.delete(id)
else this.favs.add(id)
// 触发刷新:ArkTS 对 Set 需显式更新引用(如必要)
this.favs = new Set(this.favs)
}
}
感受差异:
- ArkTS 的 类型签名 + 私有方法 + 状态引用更新 让代码更可控;
- 复杂度上来之后,架构与演进成本立刻分出高下。
6) 工程化与避坑清单:把坑踩在我这儿
6.1 架构建议(Page/Stage 皆适用)
/app # 入口、路由、全局Provider
/features
/search # 业务特性:组件+store+service
/favorites
/shared # 通用:主题、工具、类型、UI-kits
- UI 与状态解耦:纯展示组件不直接发请求
- Provider 下沉:主题/用户/权限用
@Provide/@Consume - 请求聚合:
services/统一管理,便于切换能力侧
6.2 性能&体验
- 列表 不可变更新(新数组/新引用)提高 diff 准确度
- 隐式动画优先,显式集中控制、别层层嵌套
- 大计算/IO 做防抖/节流;动画期间避免同步重排
6.3 状态规则“二三事”
- 能
@Prop + 回调就别上@Link - 跨很多层才用
@Provide/@Consume;全局才用AppStorage - 复杂对象用
@Observed/@ObjectLink,不要野蛮any
6.4 常见坑
- Set/Map 变更不刷新 → 重新赋值新实例触发更新(
this.set = new Set(this.set)) - 跨页数据丢 →
AppStorage或参数路由,别靠全局变量 - 动效掉帧 → 曲线别太“硬”、组件树别太“深”、尽量隐式动画
- 类型逐步“腐化” → 打开严格 TS 规则,公共类型抽到
/shared/types.ts
7) 结语 & 你的下一步
到这儿,“ArkUI × ArkTS”这对黄金搭档是不是顺眼多了?UI 用 ArkUI 的声明式范式保证一致性;语言选 ArkTS 获得类型与编译期红线;响应式状态与动画遵守“最少必要原则”,既稳又优雅。剩下的,就是把你的业务塞进去,然后疯狂迭代,开心打磨。😎
🧧福利赠与你🧧
无论你是计算机专业的学生,还是对编程有兴趣的小伙伴,都建议直接毫无顾忌的学习此专栏「滚雪球学SpringBoot」专栏(全网一个名),bug菌郑重承诺,凡是学习此专栏的同学,均能获取到所需的知识和技能,全网最快速入门SpringBoot,就像滚雪球一样,越滚越大, 无边无际,指数级提升。
最后,如果这篇文章对你有所帮助,帮忙给作者来个一键三连,关注、点赞、收藏,您的支持就是我坚持写作最大的动力。
同时欢迎大家关注公众号:「猿圈奇妙屋」 ,以便学习更多同类型的技术文章,免费白嫖最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板、技术文章Markdown文档等海量资料。
✨️ Who am I?
我是bug菌(全网一个名),CSDN | 掘金 | InfoQ | 51CTO | 华为云 | 阿里云 | 腾讯云 等社区博客专家,C站博客之星Top30,华为云多年度十佳博主/价值贡献奖,掘金多年度人气作者Top40,掘金等各大社区平台签约作者,51CTO年度博主Top12,掘金/InfoQ/51CTO等社区优质创作者;全网粉丝合计 30w+;更多精彩福利点击这里;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试真题、4000G PDF电子书籍、简历模板等海量资料,你想要的我都有,关键是你不来拿。

-End-
- 点赞
- 收藏
- 关注作者
评论(0)