OpenHarmony 英语学习 App 实战自定义生词本、持久化存储与学习数据管理摘要英语学习 App 不能只提供固定词库。真实学习中用户会从课本、阅读、听力、影视台词里遇到自己的生词因此“自定义生词本”是非常重要的功能。本文以「英语视界 YingYu」项目为例分享如何在 OpenHarmony/HarmonyOS 中实现自定义生词本、详情弹窗、添加表单、搜索过滤和本地持久化。相关文件包括entry/src/main/ets/pages/CustomWordBook.ets entry/src/main/ets/utils/StorageService.ts entry/src/main/ets/model/DataModels.ts entry/src/main/ets/utils/YingYuPreferences.ts一、自定义生词本要解决什么问题固定词库适合系统学习但用户自己的生词更有价值课外阅读遇到的词老师补充的重点词听力材料里的陌生词错题中反复出现的词考试作文常用表达。所以自定义生词本需要具备添加单词保存音标、释义、例句、标签查看详情搜索过滤参与复习系统本地持久化保存。二、数据模型CustomWord项目中使用CustomWord表示一个自定义单词export interface CustomWord {id:stringword:stringphonetic?:stringtranslation:stringexample?:stringtags:string[] createdAt:stringreviewCount:numbermastered:boolean}字段设计比较完整id唯一标识word英文单词phonetic可选音标translation中文释义example可选例句tags标签createdAt添加时间reviewCount复习次数mastered是否掌握。这些字段不仅能展示单词详情也能和复习系统、统计系统结合。三、存储 Key 设计StorageService.ts中集中管理存储 KeyconstSTORAGE_KEY_CUSTOM_WORDS yingyu_custom_wordsconstSTORAGE_KEY_REVIEW_RECORDS yingyu_review_recordsconstSTORAGE_KEY_DAILY_TASKS yingyu_daily_tasksconstSTORAGE_KEY_LEARNING_GOALS yingyu_learning_goals把 Key 放在统一服务里有两个好处避免页面里到处写字符串后续迁移数据结构更方便。四、生成唯一 ID添加单词时需要生成唯一 IDfunctiongenerateId():string{ returnDate.now().toString(36)Math.random().toString(36).substring(2,9) }这是一个轻量方案适合本地数据。它结合时间戳和随机字符串在单机使用场景下足够可靠。五、读取自定义单词读取逻辑封装在getCustomWords()中exportfunctiongetCustomWords():CustomWord[] {try{conststored AppStorage.getstring(STORAGE_KEY_CUSTOM_WORDS)if(stored) {returnJSON.parse(stored)asCustomWord[] } }catch(e) {console.error(Failed to load custom words:, e) }return[] }这里有一个值得保留的习惯读取失败时返回空数组而不是让页面崩掉。学习类应用要优先保证可用性。六、保存自定义单词保存时把数组序列化成 JSONexportfunctionsaveCustomWords(words: CustomWord[]): void {try{AppStorage.setOrCreate(STORAGE_KEY_CUSTOM_WORDS, JSON.stringify(words)) } catch (e) { console.error(Failedtosave custom words:, e) } }对于小规模本地生词本JSON 数组足够简单。后续如果数据量上升可以迁移到数据库或云端同步。七、添加单词补齐系统字段页面提交的只是用户输入字段真正保存前由服务层补齐 ID、创建时间、复习次数和掌握状态exportfunctionaddCustomWord(word: OmitCustomWord,id|createdAt|reviewCount|mastered):CustomWord{constnewWord:CustomWord { ...word,id:generateId(),createdAt:newDate().toISOString(),reviewCount:0,mastered:false}constwords getCustomWords() words.push(newWord)saveCustomWords(words)returnnewWord }这样页面只关心用户输入服务层负责数据完整性。八、更新和删除更新单词exportfunctionupdateCustomWord(id:string,updates: PartialCustomWord): boolean { const words getCustomWords()const index words.findIndex(ww.idid)if(index ! -1) { words[index] { ...words[index], ...updates } saveCustomWords(words)returntrue} returnfalse}删除单词exportfunctiondeleteCustomWord(id:string):boolean{constwords getCustomWords()constfiltered words.filter(ww.id! id)if(filtered.length! words.length) {saveCustomWords(filtered)returntrue}returnfalse}返回boolean可以让页面判断操作是否成功并显示 Toast 或刷新列表。九、搜索过滤搜索逻辑同时支持英文、中文释义和标签exportfunctionsearchCustomWords(keyword:string): CustomWord[]{ const words getCustomWords()const lower keyword.toLowerCase()return words.filter(w w.word.toLowerCase().includes(lower)||w.translation.toLowerCase().includes(lower)||w.tags.some(t t.toLowerCase().includes(lower)) ) }这种搜索方式对用户很友好输入英文可以找到单词输入中文可以找到释义输入标签可以找到专题词。十、添加单词表单CustomWordBook.ets中定义了AddWordSheet子组件用于处理添加表单。Componentstruct AddWordSheet {Linkvisible: booleanonWordAdded: () void () {}StatewordText: string Statephonetic: string Statetranslation: string Stateexample: string StatetagsInput: string }这里把表单做成独立子组件而不是直接放在页面里可以减少整页重建也更利于维护。提交时处理标签const tags this.tagsInput.split(,).map(t t.trim()).filter(t t.length0)标签支持逗号分隔适合用户输入“考试, 写作, 高频词”这样的内容。十一、详情弹窗WordDetailSheet项目还实现了单词详情弹窗Componentstruct WordDetailSheet {Linkvisible: booleanPropword: CustomWord { id: , word: , translation: , tags: [], createdAt: , reviewCount: 0, mastered: false } }弹窗进入时有缩放和透明度动画aboutToAppear() {this.dialogScale 0.88this.dialogOpacity 0animateTo({ duration:260, curve: Curve.EaseOut }, () {this.dialogScale 1this.dialogOpacity 1}) }展示内容包括单词音标中文释义例句标签添加时间复习次数或掌握状态。十二、本地持久化的另一层Preferences 封装项目中还有YingYuPreferences.ts用于 Preferences 和 AppStorage 的兜底封装。exportfunctioninitYingYuUserData(context: common.UIAbilityContext):void{if(pref) {return}try{ pref preferences.getPreferencesSync(context, {name:yingyu_user_data_v1}) }catch(e) {console.error(initYingYuUserData failed:,JSON.stringify(e)) } }读取时优先 Preferences失败则回退 AppStorageexportfunctionyingyuPrefGet(key:string):string|undefined{if(pref) {constv pref.getSync(key,)asstringreturnv.length0? v :undefined}constfallback AppStorage.getstring(key)if(fallback !undefined fallback.length0) {returnfallback }returnundefined}这种封装让数据层更稳也便于后续统一替换存储方案。十三、小结本文结合「英语视界 YingYu」项目拆解了自定义生词本的完整实现使用CustomWord描述用户单词使用StorageService封装增删改查使用 JSON AppStorage 保存轻量数据使用searchCustomWords()支持英文、中文、标签搜索使用AddWordSheet管理添加表单使用WordDetailSheet展示单词详情使用 Preferences 封装增强持久化可靠性。自定义生词本让学习 App 从“固定内容工具”变成“用户自己的学习资料库”。这类功能虽然不 flashy但非常能提升长期使用价值。