鸿蒙:名言生成器打造指南
一、前言
1.1 为什么写一个名言生成器
在快节奏的移动互联网时代,人们打开手机往往是为了寻求片刻的安宁。一句恰到好处的话,可能就能点亮一个人的一整天。这就是名言生成器这类 App 存在的意义——它不解决具体问题,但它提供情绪价值。
从技术角度看,名言生成器是一个"小而美"的经典练手项目:
- 数据层 —— 一个字符串数组,存储所有名言
- 逻辑层 —— 一个随机选取算法,保证不重复
- 表现层 —— 优雅的排版和动画,让文字更有力量
麻雀虽小,五脏俱全。它涵盖了 ArkTS 开发中最重要的基础能力:状态管理、数组操作、事件响应、UI 样式。更重要的是,它让我们有机会深入探讨用户体验设计——这不是一个功能驱动型 App,而是一个情绪驱动型 App,设计上需要考虑的远不止"能不能用"。
1.2 最终效果预览
先看最终要实现的页面:
+----------------------------------+
| |
| |
| "心有山海,静而不争。" |
| |
| |
| [ 换一句 ] |
| |
| 粉紫渐变背景 |
| 圆角按钮 |
| 温柔的整体风格 |
+----------------------------------+



全屏居中,一句名言 + 一个按钮。点击按钮随机切换下一句,且不会和上一句重复。背景是粉紫到浅蓝的渐变,按钮是柔和的粉色圆角设计。
1.3 本文结构
本文将按照以下顺序展开:
- 完整代码展示 —— 先看全貌,建立整体认知
- 数据设计 —— 名言列表的结构与扩展
- 随机算法 —— 从朴素实现到防重复的演进
- UI 设计 —— 从布局到配色,打造治愈感
- 状态管理 —— @State 在数组场景中的使用
- 扩展与进阶 —— 动画、分类、网络加载、本地存储
- 设计理念 —— 为什么"温柔"是一种值得追求的设计风格
- 性能与最佳实践 —— 数组操作、渲染优化、代码组织
二、完整代码
@Entry
@Component
struct QuoteGenerator {
// 名言列表(可自行扩展)
@State quoteList: string[] = [
"生活最好的状态,是冷冷清清的风风火火。",
"心有山海,静而不争。",
"慢慢来,好戏都在烟火里。",
"你若盛开,清风自来。",
"保持热爱,奔赴山海。",
"往事不回头,未来不将就。",
"心向阳光,何惧风霜。",
"平凡日子,也藏着温柔的光。",
"愿你历尽千帆,归来仍是少年。",
"人生没有白走的路,每一步都算数。"
];
// 当前显示的名言索引
@State currentIndex: number = 0;
// 随机切换名言
changeQuote() {
let newIndex: number;
// 避免和上一句重复
do {
newIndex = Math.floor(Math.random() * this.quoteList.length);
} while (newIndex === this.currentIndex);
this.currentIndex = newIndex;
}
build() {
Column({ space: 40 }) {
// 名言文本
Text(this.quoteList[this.currentIndex])
.fontSize(22)
.fontWeight(FontWeight.Medium)
.lineHeight(35)
.letterSpacing(1)
.textAlign(TextAlign.Center)
.padding(20)
// 刷新按钮
Button("换一句")
.fontSize(18)
.width(160)
.height(50)
.borderRadius(25)
.backgroundColor("#FF7A85")
.fontColor(Color.White)
.onClick(() => {
this.changeQuote();
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
// 渐变背景(粉紫到浅蓝)
.linearGradient({
direction: GradientDirection.Bottom,
colors: [[0xFFF5F7FA, 0.0], [0xFFE4EAF5, 1.0]]
})
.padding(30)
}
}

一共 57 行代码,完成了从数据定义、算法逻辑到 UI 展现的全链路。
下面逐层拆解。
三、数据设计:名言列表的结构哲学
3.1 @State 修饰数组
@State quoteList: string[] = [
"生活最好的状态,是冷冷清清的风风火火。",
"心有山海,静而不争。",
// ... 共 10 句
];
@State 修饰一个 string[] 数组。在 ArkTS 中,数组作为引用类型,其变化追踪有一些需要特别注意的地方。
直接修改数组元素会触发更新吗?
// ❌ 这样可能不会触发 UI 更新
this.quoteList[3] = "新的名言";
// ✅ 这样一定会触发
this.quoteList = [...this.quoteList];
this.quoteList[3] = "新的名言";
this.quoteList = this.quoteList.slice();
// ✅ 使用数组方法(push、splice 等通常可以触发)
this.quoteList.push("新的名言");
为什么直接下标赋值可能不触发?因为 ArkTS 的 @State 追踪的是变量的引用变化,而不是对象内部属性的变化。数组方法如 push、splice、pop 等之所以能触发,是因为框架对常见数组方法做了特殊处理——它们会修改数组的 length 和内部元素,框架检测到这些变化后触发更新。
但最稳妥的方式始终是:创建新数组赋值给 @State 变量。这保证了行为的一致性,避免踩坑。
3.2 名言的选择标准
我选的这 10 句名言有一个共同点:治愈感。
这不是偶然的。名言生成器的用户打开它,不是为了获取知识,而是为了获得情感共鸣。因此选句的标准应该是:
| 维度 | 高优先级 | 低优先级 |
|---|---|---|
| 情感价值 | 温暖的、治愈的、励志的 | 冷冰冰的道理 |
| 语言美感 | 有韵律、有意象、读起来舒服 | 直白说教 |
| 通用性 | 适合大多数人的心境 | 过于个人化 |
| 时长 | 一句话,10-20 字 | 长篇大论 |
比如"生活最好的状态,是冷冷清清的风风火火"——这句话有对比(冷冷清清 vs 风风火火),有意象(烟火人间),有态度(平淡中不失热烈),读一遍就能记住。这就是好的名言。
"人生没有白走的路,每一步都算数"——这句话有理有据,有画面感(走路),有哲理感,是典型的"金句"结构。
3.3 数据扩展策略
10 句显然不够。实际发布的名言生成器,至少需要 100 句以上才能让用户有"每次打开都不一样"的体验。
数据扩展有几个方向:
方向一:手动精选
从书籍、电影、名人演讲中摘录。优点是质量可控,缺点是工作量随数量线性增长。
方向二:爬虫采集
从名言网站抓取。但需要注意版权问题,且需要过滤质量参差不齐的内容。
方向三:AIGC 生成
用大模型批量生成金句。优点是效率极高,几分钟就能生成几百条。缺点是需要人工筛选——AI 生成的句子有时"看起来很对,读起来乏味"。
方向四:用户 UGC
让用户自行添加和分享名言。这是最能产生粘性的方式,但也需要审核机制。
在工程实现上,随着数据量增加,数据存储方式也需要升级:
10 条 → 硬编码在代码中
50 条 → 单独的 JSON 配置文件
200 条 → 应用内 SQLite 数据库 / 本地文件
1000+ 条 → 云端 API + 本地缓存
四、随机算法:从朴素到优雅
4.1 核心逻辑
changeQuote() {
let newIndex: number;
do {
newIndex = Math.floor(Math.random() * this.quoteList.length);
} while (newIndex === this.currentIndex);
this.currentIndex = newIndex;
}
这段代码虽然短,但包含了一个完整的防重复随机算法。
4.1.1 Math.random() 的工作原理
Math.random() 返回一个 [0, 1) 之间的浮点数,服从均匀分布。这意味着理论上每个值出现的概率相等。
// 将 [0, 1) 映射到 [0, length)
Math.random() * this.quoteList.length
// 例如 length=10,生成 [0, 10) 的浮点数
// 取整得到整数索引
Math.floor(Math.random() * this.quoteList.length)
// 等概率生成 0, 1, 2, ..., 9
Math.floor 和 Math.ceil 的区别:
Math.random() * 10
生成的浮点数:0.0 ~ 9.999...
Math.floor(...) → 0, 1, 2, ..., 9 (均匀 10 个值)
Math.ceil(...) → 0, 1, 2, ..., 10 (不均匀,0 的概率极低)
所以这里用 Math.floor 是正确的。
4.1.2 do...while 防重复
do {
newIndex = Math.floor(Math.random() * this.quoteList.length);
} while (newIndex === this.currentIndex);
这个循环保证:新生成的索引不等于当前索引。
这是一个 rejection sampling(拒绝采样) 的典型例子——反复采样直到满足条件。
当列表数量 n=10 时:
- 每次采样不重复的概率 = 9/10 = 90%
- 期望循环次数 = 1/(9/10) ≈ 1.11 次
- 最坏情况(理论上一直重复)概率趋近于 0
当列表数量 n=2 时:
- 每次采样不重复的概率 = 1/2 = 50%
- 期望循环次数 = 2 次
- 还行,性能开销可以忽略
所以这个算法在列表数量 >= 3 时非常高效。
4.1.3 更严格的不重复算法
如果我们想要更严格的随机性——比如让用户看完所有名言之前不重复任何一句——那就需要 Fisher-Yates 洗牌算法了:
@State shuffledList: string[] = []
@State shuffleIndex: number = 0
aboutToAppear() {
this.resetShuffle()
}
resetShuffle() {
this.shuffledList = [...this.quoteList]
// Fisher-Yates 洗牌
for (let i = this.shuffledList.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[this.shuffledList[i], this.shuffledList[j]] = [this.shuffledList[j], this.shuffledList[i]]
}
this.shuffleIndex = 0
}
changeQuote() {
this.shuffleIndex++
if (this.shuffleIndex >= this.shuffledList.length) {
this.resetShuffle()
}
// 注意:这里 currentIndex 不再是指向 quoteList 的索引
// 而是直接获取 shuffledList 中的名言
}
build() {
Text(this.shuffledList[this.shuffleIndex])
// ...
}
这个方案的优点:
- 用户看到所有名言之前,绝对不会重复
- 每一轮的顺序都是随机的
- 一轮结束后自动洗牌重新开始
缺点:
- 需要额外维护一个打乱后的数组
- 需要管理洗牌周期
不过对于名言生成器这种使用场景,简单的 do...while 防重复已经足够。用户不会连续点 100 次按钮——他们通常点一两次,找到触动自己的那句话就够了。
五、UI 设计:为什么粉紫渐变配粉色按钮是"治愈感"的组合
这是整篇文章最具主观性但也最重要的一节。
.linearGradient({
direction: GradientDirection.Bottom,
colors: [[0xFFF5F7FA, 0.0], [0xFFE4EAF5, 1.0]]
})
Button("换一句")
.borderRadius(25)
.backgroundColor("#FF7A85")
我们逐一分析每个设计决策。
5.1 渐变色分析
背景使用了从上到下的渐变,从 #F5F7FA(近乎白色的浅灰蓝)到 #E4EAF5(淡淡的薰衣草紫/蓝)。
这两个颜色在色彩学上属于低饱和度、高明度的色系。它们的特点是:
- 不刺眼 —— 高亮度但低饱和度的颜色不会刺激视觉神经,适合长时间注视
- 有质感 —— 渐变让平面有了"深度",不再单调
- 情绪暗示 —— 蓝色系给人冷静、安定的感觉;紫色系带有一点点浪漫和温柔
从心理学角度看,冷暖色调融合(蓝色偏冷,薰衣草偏暖)创造了一种平衡感——既有蓝天的开阔,又有花香的温暖。
gradient 的方向是 GradientDirection.Bottom,即从上到下。这意味着在手机竖屏时,浅色在上方,较深(其实也还是很浅)的紫色在下方。这模拟了自然界中天空上亮下暗的规律,符合人的视觉直觉。
5.2 按钮色彩分析
按钮颜色 #FF7A85 是一种温柔的珊瑚粉/玫瑰粉。
为什么选择粉色系?
- 情感关联 —— 粉色在色彩心理学中与"温柔、关爱、温暖"相关联
- 互补色 —— 粉色(偏红)与背景的蓝紫色在色环上邻近,属于类似色搭配,和谐不冲突
- 品牌感 —— 这种粉色不是荧光粉(太跳),也不是暗粉(太沉闷),而是恰到好处的"让人想点击"的粉
5.3 圆角设计
.borderRadius(25)
按钮的圆角半径为 25,而按钮高度是 50。25 恰好是高度的一半——这意味着按钮两端是完整的半圆,也就是所谓的"胶囊按钮"(Capsule Button)。
圆角在 UI 设计中的意义:
| 圆角大小 | 传达的感觉 | 典型场景 |
|---|---|---|
| 0(直角) | 严谨、硬核 | 工具型 App、数据面板 |
| 4-8 | 标准、中性 | 表单、卡片 |
| 12-16 | 柔和、友好 | 社交、内容型 App |
| 25+(胶囊) | 温暖、亲和 | 情感化、女性向、治愈系 |
胶囊按钮在视觉上消除了"尖锐感",让人更容易产生点击的欲望。这就是为什么很多情感类 App 都用圆形或胶囊按钮——它们看起来不那么像机器,更像一个邀请。
5.4 文字排版
文字排版的每个属性都有其用意:
- fontSize(22) —— 正文阅读的舒适字号,不大不小,不卑不亢
- fontWeight(FontWeight.Medium) —— 中粗字重,比常规粗一点,比粗体细一点,有分量感但不压迫
- lineHeight(35) —— 行高约 1.6 倍字号,留白充裕,读起来不拥挤
- letterSpacing(1) —— 字间距加 1,让文字更"透气",有时间慢慢品味
- textAlign(TextAlign.Center) —— 居中排版,对称、庄重、聚焦
- padding(20) —— 左右留白,防止文字贴边
如果你在手机上打开一个名言生成器,看到这样排版的文字,你的第一感觉应该是"安静"——这就是排版要达到的效果:让文字本身成为视觉主体,而不是排版技巧。
5.5 间距与整体平衡
Column({ space: 40 })
Copy
文字和按钮之间的间距是 40。这个间距足够大,让两者各自独立成"块";又不会太大,导致页面显得松散。
为什么是 40 而不是 20 或 60?在 UI 设计中,间距的选择通常遵循 8 点网格体系:
间距 = 8 × n
常见值:8, 16, 24, 32, 40, 48, 56, 64
40 = 8 × 5,在这个体系中属于"中等偏大"的间距,适合分隔不同功能区域(文字区 vs 操作区)。
六、状态管理:@State 在 QuoteGenerator 中的应用
6.1 两个状态的角色
@State quoteList: string[] = [...] // 数据源
@State currentIndex: number = 0 // 当前索引
这两个状态的职责非常清晰:
quoteList—— 只读数据,通常不修改(除非用户增删名言)currentIndex—— 唯一的可变状态,驱动 UI 变化
这种一个可变状态驱动 UI的模式,是 ArkTS 中最简单的状态管理模型。数据流是单向的:
用户点击 → changeQuote() → this.currentIndex 变化 → UI 重新渲染
整个数据流清晰可追溯,没有复杂的多状态联动,没有跨组件数据同步。这是最简单的状态管理模式,也是最适合这个场景的模式。
6.2 @State 变化触发的流程
当用户点击按钮时:
onClick回调执行this.changeQuote()changeQuote()内部修改this.currentIndex- ArkTS 运行时检测到
@State currentIndex变化 - 框架查找所有读取了
currentIndex的 UI 节点 - 只重新渲染这些节点(在这里就是
Text(this.quoteList[this.currentIndex])) - 文字更新,按钮不变,背景不变
这个流程的核心优势是精准——每次点击只更新文字本身,不会触发按钮或背景的重新渲染。在 ArkTS 中,这种依赖追踪在编译期就完成了,所以运行时代价极低。
6.3 状态不可变性原则
虽然 ArkTS 的 @State 可以追踪数组内部变化,但良好的实践是始终将状态视为不可变的。
// 推荐的做法:创建新数组
this.quoteList = [...this.quoteList, "新的名言"]
// 不推荐的做法:直接修改原数组再赋值
this.quoteList.push("新的名言")
this.quoteList = this.quoteList // 可能不会触发更新
不可变性原则带来的好处:
- 可预测性 —— 状态变化可追踪,知道什么时候变了、变成了什么
- 调试友好 —— 可以轻易地比较前后状态
- 性能清晰 —— 新引用一定触发更新,不会出现"改了但没刷新"的问题
七、扩展与进阶
基础功能完成后,我们可以从各个方向扩展这个应用。
7.1 添加切换动画
目前的切换是瞬变的,加上动画会大幅提升体验:
@State quoteOpacity: number = 1
changeQuote() {
// 先淡出
this.quoteOpacity = 0
// 200ms 后换内容再淡入
setTimeout(() => {
let newIndex: number
do {
newIndex = Math.floor(Math.random() * this.quoteList.length)
} while (newIndex === this.currentIndex)
this.currentIndex = newIndex
// 触发淡入
this.quoteOpacity = 1
}, 200)
}
build() {
Text(this.quoteList[this.currentIndex])
// ...原有样式不变...
.opacity(this.quoteOpacity)
.animation({
duration: 300,
curve: Curve.EaseInOut
})
}
这样切换到下一句时,旧文字淡出,新文字淡入,过渡自然。300ms 的动画时长是人眼感觉"流畅但不拖沓"的最佳区间。
还可以用转场动画实现更丰富的效果:
// 从下方滑入
Text(this.quoteList[this.currentIndex])
.transition(TransitionEffect.translate({ y: 20 }).combine(TransitionEffect.opacity(0)))
或者模仿翻页效果:
// 3D 旋转翻页
Text(this.quoteList[this.currentIndex])
.rotate({ x: 0, y: 1, z: 0, angle: this.rotateAngle })
动画是名言生成器的灵魂——物理切换瞬间完成,但情感需要时间过渡。
7.2 分类功能
名言可以按分类展示,让用户选择当前需要的"情绪":
interface QuoteCategory {
name: string
icon: string
quotes: string[]
}
@State categories: QuoteCategory[] = [
{
name: '治愈',
icon: '☀️',
quotes: ['心有山海,静而不争。', '慢慢来,好戏都在烟火里。', ...]
},
{
name: '励志',
icon: '🚀',
quotes: ['保持热爱,奔赴山海。', '人生没有白走的路。', ...]
},
{
name: '爱情',
icon: '💕',
quotes: ['你若盛开,清风自来。', ...]
},
{
name: '生活',
icon: '🍵',
quotes: ['平凡日子,也藏着温柔的光。', ...]
}
]
@State currentCategoryIndex: number = 0
@State currentQuoteIndex: number = 0
get currentCategory(): QuoteCategory {
return this.categories[this.currentCategoryIndex]
}
changeQuote() {
const quotes = this.currentCategory.quotes
let newIndex: number
do {
newIndex = Math.floor(Math.random() * quotes.length)
} while (newIndex === this.currentQuoteIndex)
this.currentQuoteIndex = newIndex
}
UI 上增加分类选择器:
Row({ space: 12 }) {
ForEach(this.categories, (cat: QuoteCategory, index: number) => {
Text(cat.icon)
.fontSize(28)
.opacity(index === this.currentCategoryIndex ? 1.0 : 0.4)
.onClick(() => {
this.currentCategoryIndex = index
this.currentQuoteIndex = 0
})
})
}
这样用户可以在不同情绪主题间切换,应用的可玩性和实用性都大幅提升。
7.3 网络加载更多名言
当本地名言不够用时,可以接入远程 API:
@State isLoading: boolean = false
async fetchMoreQuotes() {
this.isLoading = true
try {
const response = await fetch('https://api.quote.example.com/quotes?count=20')
const data: string[] = await response.json()
// 合并到本地列表
const existing = new Set(this.quoteList)
const newQuotes = data.filter(q => !existing.has(q))
this.quoteList = [...this.quoteList, ...newQuotes]
} catch (e) {
console.error('获取名言失败', e)
// 降级:使用本地缓存
} finally {
this.isLoading = false
}
}
这里有一个重要的设计细节:去重。网络获取的名言可能与本地已有内容重复,用 Set 做一次过滤,确保列表中没有重复项。
7.4 本地缓存与收藏
用户可能想把某句名言保存下来:
@State favorites: string[] = []
// 收藏当前名言
toggleFavorite() {
const current = this.quoteList[this.currentIndex]
const idx = this.favorites.indexOf(current)
if (idx >= 0) {
this.favorites.splice(idx, 1) // 取消收藏
} else {
this.favorites.push(current) // 收藏
}
// 持久化到本地存储
AppStorage.setOrCreate('favorites', JSON.stringify(this.favorites))
}
// 应用启动时加载收藏
aboutToAppear() {
const saved = AppStorage.get<string>('favorites')
if (saved) {
this.favorites = JSON.parse(saved)
}
}
ArkTS 提供了 AppStorage 作为全局持久化存储,适合保存用户设置、收藏等轻量级数据。对于更大的数据集(如用户自己导入的数百条名言),推荐使用 Preferences 或关系型数据库。
7.5 分享功能
名言生成器天然适合社交分享:
// 生成分享图片
@State shareImage: PixelMap | null = null
async generateShareImage() {
const context = getContext(this)
const imageSource = await context.createImage(this.quoteList[this.currentIndex], {
width: 1080,
height: 1920,
background: '#F5F7FA',
fontColor: '#333333',
fontSize: 48,
lineHeight: 72,
padding: 80
})
this.shareImage = imageSource
// 调起分享面板
context.share(this.shareImage)
}
在鸿蒙系统中,分享通过 context.share() 方法调起系统分享面板,用户可以选择保存到相册、发送到微信/微博等。
7.6 自动轮播模式
用户可能希望名言自动切换,就像桌面小组件一样:
@State autoPlay: boolean = false
@State timerId: number = 0
@State interval: number = 5000 // 5秒
toggleAutoPlay() {
this.autoPlay = !this.autoPlay
if (this.autoPlay) {
this.timerId = setInterval(() => {
this.changeQuote()
}, this.interval)
} else {
clearInterval(this.timerId)
}
}
// 组件销毁时清理定时器
aboutToDisappear() {
if (this.timerId) {
clearInterval(this.timerId)
}
}
自动轮播模式让名言生成器变成了一个"桌面冥想伴侣"——放在桌上,每隔几秒自动切换一句,营造沉浸的氛围。
需要注意的是:ArkTS 的 setInterval 在不作为前台应用运行时可能会被系统限制频率或暂停。如果要实现后台持久轮播,需要使用 BackgroundTaskManager API 申请后台任务权限。
八、深度思考:设计理念与情绪价值
8.1 为什么"温柔"是一种设计风格
名言生成器这类 App 本质上卖的不是功能,是情绪。用户安装它,是因为在某个时刻感到迷茫、焦虑、或者孤独,需要一句话来给自己一点力量。
因此,整个应用的 UI 设计都必须服务于一个核心目标:让用户感到被理解、被安慰。
这就是为什么:
- 背景用粉紫渐变,而不是黑底金字的"硬核"风格
- 按钮用珊瑚粉胶囊,而不是棱角分明的矩形按钮
- 文字字号 22、字间距 1、行高 35,一切都是为了"读起来舒服"
- 选的名言都是温暖的、鼓励的,没有说教的、批判的
这些看似随意的设计决策,背后都有一个共同的出发点:共情。
8.2 功能减法
在开发过程中,一个常见的错误是不断加功能:
- "加个倒计时吧"
- "加个每日签到吧"
- "加个社交广场吧"
- "加个会员付费吧"
每个功能单独看都有道理,但加在一起就变成了一个"四不像"——用户打开它,看到的是满屏的功能入口,而不是那句他们真正想要的话。
名言生成器的核心竞争力是简单。打开即读,读完即走。任何一个多余的功能都在削弱这种纯粹感。
这就是功能减法的理念:
问:用户打开 App 想做什么?
答:看到一句有共鸣的话。
问:什么会阻碍这个目标?
答:广告、弹窗、注册、引导、复杂交互。
问:我们能做什么来强化这个目标?
答:让文字更美、切换更流畅、内容更丰富(但交互不复杂)。
8.3 什么是"好的"名言列表
最后,关于名言本身。一份好的名言列表,应该具备以下特征:
- 多样性 —— 治愈、励志、生活、爱情,覆盖不同心境
- 真实性 —— 有出处的名言比"网传"更有力量
- 适度性 —— 不要过于沉重,也不要过于轻浮
- 时代感 —— 经典名言 + 当代金句,老中青都能找到共鸣
- 语言美感 —— 读起来有节奏感、有意象、有余韵
比如"生活最好的状态,是冷冷清清的风风火火"——它用矛盾修辞(冷冷清清 vs 风风火火)制造出张力,让人过目不忘。这就是语言的美感。
又如"愿你历尽千帆,归来仍是少年"——它用"千帆"(经历)和"少年"(初心)形成对比,表达了对初心不改的美好祝愿。一句话里有故事、有画面、有情感。
选名言看似简单,实则考验的是对人性的理解——哪些话能戳中人心,哪些话只是漂亮话。这个能力,算法学不来,只有人能做到。
九、性能优化与最佳实践
9.1 数组操作性能
名言列表的常见操作和性能:
| 操作 | 时间复杂度 | 内存开销 | 备注 |
|---|---|---|---|
| 随机选取 | O(1) | 极小 | 核心操作,必须快 |
| 添加一条 | O(1) | 小 | push 或 spread |
| 删除一条 | O(n) | 中等 | 需要遍历查找+删除 |
| 搜索 | O(n) | 无 | 无索引时需遍历 |
| 排序 | O(n log n) | 中等 | 如需按分类排序 |
对于名言列表这种数据量(通常 < 10000 条),所有操作都是毫秒级的,不需要特殊优化。
但如果列表增长到数万级别(比如从网络API批量导入),就需要考虑:
// 使用 ArrayBuffer 或 TypedArray 存储
// 使用虚拟列表渲染(LazyForEach)
LazyForEach(this.quoteDataSource, (quote: string, index: number) => {
Text(quote)
.fontSize(20)
}, (quote: string) => quote)
LazyForEach 只渲染当前可见区域的项,滚动时动态回收和创建。对于超长列表,这是唯一正确的渲染方式。
9.2 避免重复渲染
当前实现中,每次点击按钮只修改 currentIndex。但 currentIndex 的变化会触发整个 build() 方法重新执行吗?
答案是:不会。ArkTS 的 @State 追踪的是读取了该状态的 UI 节点。
所以实际上,每次点击只有 Text 组件及其属性会重新渲染。Column、Button、背景全部跳过——这就是细粒度响应式的性能优势。
9.3 代码组织最佳实践
随着功能增加,应该将代码拆分为独立的文件:
pages/
QuoteGenerator.ets ← 主页面,组装各组件
components/
QuoteText.ets ← 名言文本组件(含动画)
CategorySelector.ets ← 分类选择器
ActionButtons.ets ← 操作按钮(换一句、收藏、分享)
models/
QuoteData.ets ← 数据定义和接口
data/
quotes.json ← 名言数据(可网络更新)
utils/
RandomUtils.ets ← 随机算法工具
StorageUtils.ets ← 本地存储工具
每个文件职责单一,易于测试和维护。
十、总结
10.1 核心要点回顾
这篇文章从一个简单的名言生成器出发,覆盖了以下知识点:
- 数据层 —— @State 数组的定义和管理,数据扩展策略
- 算法层 —— do...while 防重复随机算法,Fisher-Yates 洗牌算法
- UI 层 —— 渐变背景、胶囊按钮、文字排版、间距系统
- 设计理念 —— 情绪价值、功能减法、治愈感设计
- 扩展方向 —— 动画、分类、网络、收藏、分享、自动轮播
- 性能优化 —— 细粒度渲染、数组操作复杂度、LazyForEach
- 代码组织 —— 组件拆分、职责划分、目录结构
10.2 代码之美
最后,让我们再读一遍最开始的 57 行代码:
@Entry
@Component
struct QuoteGenerator {
@State quoteList: string[] = [
"生活最好的状态,是冷冷清清的风风火火。",
"心有山海,静而不争。",
"慢慢来,好戏都在烟火里。",
"你若盛开,清风自来。",
"保持热爱,奔赴山海。",
"往事不回头,未来不将就。",
"心向阳光,何惧风霜。",
"平凡日子,也藏着温柔的光。",
"愿你历尽千帆,归来仍是少年。",
"人生没有白走的路,每一步都算数。"
];
@State currentIndex: number = 0;
changeQuote() {
let newIndex: number;
do {
newIndex = Math.floor(Math.random() * this.quoteList.length);
} while (newIndex === this.currentIndex);
this.currentIndex = newIndex;
}
build() {
Column({ space: 40 }) {
Text(this.quoteList[this.currentIndex])
.fontSize(22)
.fontWeight(FontWeight.Medium)
.lineHeight(35)
.letterSpacing(1)
.textAlign(TextAlign.Center)
.padding(20)
Button("换一句")
.fontSize(18)
.width(160)
.height(50)
.borderRadius(25)
.backgroundColor("#FF7A85")
.fontColor(Color.White)
.onClick(() => {
this.changeQuote();
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.linearGradient({
direction: GradientDirection.Bottom,
colors: [[0xFFF5F7FA, 0.0], [0xFFE4EAF5, 1.0]]
})
.padding(30)
}
}

更多推荐



所有评论(0)