
文章目录前言状态设计最小化原则完整实现PC端新闻筛选页为什么 get 比维护第二个数组好搜索和分类筛选叠加写在最后前言做带分类标签的新闻/内容列表很多人的第一反应是维护两个数组一个完整数据源一个过滤后的展示列表。每次切换分类时手动更新第二个数组。这个方案能跑但是多余的。ArkUI 有更简洁的做法只维护activeCategory这一个状态通过get访问器派生出过滤列表模板里直接用派生属性。切换分类只需要改一个变量其他的交给框架。状态设计最小化原则先定义数据结构interfaceNewsItem{id:numbertitle:stringcategory:stringsource:stringtime:stringemoji:stringreadCount:number}组件状态只需要一个StateactiveCategory:string全部派生属性用getgetfilteredNews():NewsItem[]{if(this.activeCategory全部)returnthis.allNewsreturnthis.allNews.filter(nn.categorythis.activeCategory)}getcategories():string[]{constallthis.allNews.map(nn.category)return[全部,...newSet(all)]}get categories()用Set自动去重提取所有分类新增数据时分类列表自动更新不需要手动维护分类数组。完整实现PC端新闻筛选页完整示例PcNewsFilterPage.etsimport{router}fromkit.ArkUIinterfaceNewsItem{id:numbertitle:stringdesc:stringcategory:stringsource:stringtime:stringemoji:stringreadCount:numberisLiked:boolean}EntryComponentstruct PcNewsFilterPage{StateactiveCategory:string全部StatelikedIds:number[][]StatesearchText:stringprivateallNews:NewsItem[][{id:1,title:HarmonyOS NEXT 发布鸿蒙操作系统的里程碑,desc:华为正式发布新一代鸿蒙操作系统标志着全面去安卓化的完成。,category:科技,source:华为官方,time:10分钟前,emoji:,readCount:12600,isLiked:false},{id:2,title:ArkUI 动效系统升级新增弹簧动画和路径动画,desc:最新版本的 ArkUI 框架带来了更丰富的动效能力。,category:开发,source:开发者社区,time:1小时前,emoji:✨,readCount:5600,isLiked:false},{id:3,title:鸿蒙 PC 版适配指南正式上线,desc:官方针对大屏设备发布完整适配规范涵盖窗口管理与键盘适配。,category:开发,source:官方文档,time:2小时前,emoji:,readCount:4200,isLiked:false},{id:4,title:鸿蒙生态应用数量突破十万,desc:覆盖金融、出行、购物等主流场景生态建设取得重大突破。,category:产品,source:鸿蒙生态,time:3小时前,emoji:,readCount:8900,isLiked:false},{id:5,title:智能家居设备接入鸿蒙生态实战,desc:详解如何将 IoT 设备接入鸿蒙分布式能力框架。,category:开发,source:技术博客,time:5小时前,emoji:,readCount:3100,isLiked:false},{id:6,title:国产操作系统生态对比2024年度盘点,desc:鸿蒙、统信、麒麟……国产 OS 的竞争格局正在发生变化。,category:科技,source:科技评论,time:1天前,emoji:,readCount:9800,isLiked:false},]getcategories():string[]{constcategories:string[][全部]this.allNews.forEach((news:NewsItem){if(!categories.includes(news.category)){categories.push(news.category)}})returncategories}privategetFilteredNews():NewsItem[]{letresult:NewsItem[]this.allNews??[]if(this.activeCategory!全部){resultresult.filter((n:NewsItem)n.categorythis.activeCategory)}consttextthis.searchText??if(text.trim()){constkeywordtext.trim().toLowerCase()resultresult.filter((n:NewsItem)n.title.toLowerCase().includes(keyword)||n.desc.toLowerCase().includes(keyword))}returnresult}toggleLike(id:number){if(this.likedIds.includes(id)){this.likedIdsthis.likedIds.filter(ii!id)}else{this.likedIds.push(id)this.likedIdsthis.likedIds.slice()}}BuildercategoryTab(cat:string){Text(cat).fontSize(13).fontColor(this.activeCategorycat?#3B82F6:#6B7280).fontWeight(this.activeCategorycat?FontWeight.Medium:FontWeight.Normal).padding({left:14,right:14,top:7,bottom:7}).backgroundColor(this.activeCategorycat?#EFF6FF:Color.Transparent).borderRadius(20).border(this.activeCategorycat?{width:1,color:#BFDBFE}:{width:0,color:Color.Transparent}).animation({duration:150,curve:Curve.EaseOut}).onClick((){this.activeCategorycat})}BuildernewsCard(news:NewsItem){Column({space:8}){// 标题行Row({space:10}){Text(news.emoji).fontSize(20).width(36).height(36).textAlign(TextAlign.Center).backgroundColor(#F3F4F6).borderRadius(8)Column({space:4}){Text(news.title).fontSize(14).fontColor(#111827).fontWeight(FontWeight.Medium).maxLines(2).textOverflow({overflow:TextOverflow.Ellipsis})Text(news.desc).fontSize(12).fontColor(#6B7280).maxLines(1).textOverflow({overflow:TextOverflow.Ellipsis})}.layoutWeight(1).alignItems(HorizontalAlign.Start)}// 底部元信息行Row(){Text(news.category).fontSize(10).fontColor(#3B82F6).backgroundColor(#EFF6FF).borderRadius(4).padding({left:6,right:6,top:2,bottom:2})Text(news.source).fontSize(11).fontColor(#9CA3AF).margin({left:8})Text(·).fontSize(11).fontColor(#D1D5DB).margin({left:4,right:4})Text(news.time).fontSize(11).fontColor(#9CA3AF)Blank()Text(${news.readCount999?(news.readCount/1000).toFixed(1)k:news.readCount}).fontSize(11).fontColor(#9CA3AF).margin({right:12})Text(this.likedIds.includes(news.id)?❤️:).fontSize(14).onClick(()this.toggleLike(news.id))}.width(100%)}.width(100%).padding({left:20,right:20,top:14,bottom:14}).backgroundColor(Color.White).border({width:{bottom:1},color:#F9FAFB})}build(){Column(){// 顶部搜索 分类标签Column({space:12}){// 搜索框Row({space:10}){Text().fontSize(15)TextInput({placeholder:搜索文章标题或摘要,text:this.searchText}).layoutWeight(1).height(36).fontSize(13).backgroundColor(Color.Transparent).onChange((v)this.searchTextv)if(this.searchText){Text(✕).fontSize(13).fontColor(#9CA3AF).onClick(()this.searchText)}}.width(100%).height(40).padding({left:12,right:12}).backgroundColor(#F3F4F6).borderRadius(20)// 分类标签横向滚动Scroll(){Row({space:6}){ForEach(this.categories,(cat:string){this.categoryTab(cat)})}.padding({left:4,right:4})}.scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off)}.padding({left:20,right:20,top:16,bottom:0}).backgroundColor(Color.White)// 结果数量提示Row(){Text(this.getFilteredNews().length0?共${this.getFilteredNews().length}篇:没有找到相关文章).fontSize(13).fontColor(#9CA3AF)}.width(100%).padding({left:20,right:20,top:12,bottom:8})// 文章列表Scroll(){Column({space:0}){ForEach(this.getFilteredNews(),(news:NewsItem){this.newsCard(news)})if(this.getFilteredNews().length0){Column({space:12}){Text().fontSize(48)Text(暂无相关内容).fontSize(16).fontColor(#9CA3AF)Text(换个关键词试试).fontSize(13).fontColor(#D1D5DB)}.width(100%).padding({top:80}).alignItems(HorizontalAlign.Center)}}}.layoutWeight(1).scrollBar(BarState.Off)}.width(100%).height(100%).backgroundColor(#F9FAFB)}}为什么 get 比维护第二个数组好维护两个数组的问题在于状态同步。每次添加/删除/修改数据你需要同时更新原始数组和过滤数组任何一处遗漏都会导致数据不一致。用get派生就没这个问题——派生属性是实时计算的只要状态activeCategory或allNews变化filteredNews下次访问时就自动重算。你只需要维护一份真实数据。// ❌ 维护两个数组StateallNews:NewsItem[][...]StatefilteredNews:NewsItem[][...]// 重复数据需要同步// ✅ 一个数组 一个派生privateallNews:NewsItem[][...]getfilteredNews():NewsItem[]{returnthis.allNews.filter(...)// 实时计算永远准确}搜索和分类筛选叠加get filteredNews里把分类和关键词两个过滤条件串联先按分类过滤再按关键词过滤。getfilteredNews():NewsItem[]{letresultthis.allNewsif(this.activeCategory!全部){resultresult.filter(nn.categorythis.activeCategory)}if(this.searchText.trim()){constkeywordthis.searchText.trim().toLowerCase()resultresult.filter(nn.title.toLowerCase().includes(keyword))}returnresult}逻辑清晰条件顺序不影响结果。如果后续要加排序、分页等也只需要在这个get里继续处理不影响其他任何地方。写在最后筛选页的状态设计原则很简单状态存最小集合其他全部派生。activeCategory是最小集合filteredNews和categories都是派生。少维护一个状态就少一处出错的可能。