当前位置: 首页 > news >正文

HarmonyOS7 跨层购物车页实战:@Provide 与 @Consume 不只是会用,还要用得顺手

文章目录

      • 共享状态只放真正共享的东西
      • 判断是否已加购很关键
      • `@Provide` 和 `@Consume` 的使用边界
      • 页面结构怎么看
      • 完整代码
      • 写在最后

购物车是最适合讲跨层状态的场景之一。顶部角标要知道购物车数量,商品列表要能加购,底部结算区还要算合计。如果每一层都手动传cartItems,页面一复杂,参数会传得很难看。

这个 HarmonyOS7 案例用@Provide@Consume把购物车状态放在祖先组件里,后代组件按需消费。它解决的不是“少写两行代码”,而是让跨层数据有一个稳定来源。

共享状态只放真正共享的东西

页面里的核心数据结构是CartEntry,包含商品 id、名称、价格、数量和图标。这个结构会同时被角标、商品区和购物车区域使用,所以适合放进跨层共享状态。

但不是所有状态都应该这么做。比如某个按钮的按下态、某个局部弹窗的显隐,就没必要放进@Provide。共享状态越多,后面越难判断谁改了它。

判断是否已加购很关键

isInCart()很短,但它决定了商品按钮展示“加入购物车”还是“已加入”。这种判断建议单独封装,不要在 UI 表达式里重复写。

isInCart(id:number):boolean{returnthis.cartItems.some((e:CartEntry)=>e.id===id)}

配合getQty()addToCart(),商品区就能在不关心父组件层级的情况下读到购物车状态。角标组件CartBadge也一样,它只消费cartItems,然后自己计算总数量。

@Provide@Consume的使用边界

这个案例适合跨层共享,不适合跨页面全局共享。也就是说,它更像页面内部的上下文,而不是应用级状态管理。如果购物车要跨多个页面同步,后面还需要配合持久化或全局状态方案。

在一个页面内部,@Provide('cartItems')提供数据,深层组件用@Consume('cartItems')拿数据。中间层不用一层层转发参数,结构会干净很多。

页面结构怎么看

SharedCartContextPage是状态提供方,CartBadge和商品区域是消费方。读代码时先找@Provide,再找@Consume,最后看addToCart()clearCart()怎么修改同一份数据。

如果你准备做商品详情、购物车角标、收藏夹数量这类功能,这个例子可以直接当页面级共享状态模板。

完整代码

下面是完整代码。入口组件名是SharedCartContextPage,可以作为 HarmonyOS7 页面内跨层状态共享的参考。

// @Provide 与 @Consume 跨层数据共享// 购物车状态(跨组件共享)// 说明:@Provide/@Consume 实现祖先到后代的数据共享,无需逐层传递interfaceCartEntry{id:numbername:stringprice:numberqty:numbericon:string}// 深层嵌套子组件:购物车角标(消费 cart 数据)@Componentstruct CartBadge{@Consume('cartItems')cartItems:CartEntry[]gettotalCount():number{returnthis.cartItems.reduce((s:number,e:CartEntry)=>s+e.qty,0)}build(){Stack({alignContent:Alignment.TopEnd}){Text('🛒').fontSize(26)if(this.totalCount>0){Text(this.totalCount>99?'99+':this.totalCount.toString()).fontSize(10).fontColor(Color.White).backgroundColor('#EF4444').borderRadius(9).width(18).height(18).textAlign(TextAlign.Center).offset({x:6,y:-6})}}.width(40).height(40)}}// 中间层组件:商品分类(不持有购物车状态,只透传)@Componentstruct ProductSection{@Consume('cartItems')cartItems:CartEntry[]title:string=''items:CartEntry[]=[]isInCart(id:number):boolean{returnthis.cartItems.some((e:CartEntry)=>e.id===id)}getQty(id:number):number{constfound=this.cartItems.find((e:CartEntry)=>e.id===id)returnfound?found.qty:0}addToCart(item:CartEntry){constidx=this.cartItems.findIndex((e:CartEntry)=>e.id===item.id)if(idx>=0){this.cartItems=this.cartItems.map((e:CartEntry,i:number)=>{if(i===idx){return{id:e.id,name:e.name,price:e.price,qty:e.qty+1,icon:e.icon}}returne})}else{item.qty=1;this.cartItems=this.cartItems.concat([item])}}build(){Column({space:8}){Text(this.title).fontSize(14).fontWeight(FontWeight.Bold).fontColor('#374151').width('100%')ForEach(this.items,(item:CartEntry)=>{Row({space:12}){Text(item.icon).fontSize(28).width(48).height(48).textAlign(TextAlign.Center).backgroundColor('#F9FAFB').borderRadius(10)Column({space:4}){Text(item.name).fontSize(14).fontWeight(FontWeight.Medium).fontColor('#111827')Text('¥'+item.price.toFixed(2)).fontSize(14).fontColor('#EF4444').fontWeight(FontWeight.Bold)}.layoutWeight(1).alignItems(HorizontalAlign.Start)if(this.isInCart(item.id)){Row({space:6}){Button('−').fontSize(14).width(28).height(28).borderRadius(14).fontColor('#374151').backgroundColor('#F3F4F6').padding(0).onClick(()=>{constidx=this.cartItems.findIndex((e:CartEntry)=>e.id===item.id)if(idx>=0){constnewQty=this.cartItems[idx].qty-1if(newQty<=0){this.cartItems=this.cartItems.filter((e:CartEntry)=>e.id!==item.id)}else{this.cartItems=this.cartItems.map((e:CartEntry,i:number)=>{if(i===idx){return{id:e.id,name:e.name,price:e.price,qty:newQty,icon:e.icon}}returne})}}})Text(this.getQty(item.id).toString()).fontSize(14).fontWeight(FontWeight.Bold).fontColor('#111827').width(24).textAlign(TextAlign.Center)Button('+').fontSize(14).width(28).height(28).borderRadius(14).fontColor('#2563EB').backgroundColor('#EFF6FF').padding(0).onClick(()=>this.addToCart(item))}.alignItems(VerticalAlign.Center)}else{Button('+加入').fontSize(12).fontColor(Color.White).backgroundColor('#2563EB').borderRadius(16).padding({left:12,right:12,top:6,bottom:6}).onClick(()=>this.addToCart(item))}}.width('100%').padding({left:14,right:14,top:10,bottom:10}).backgroundColor(Color.White).borderRadius(10).alignItems(VerticalAlign.Center)},(item:CartEntry)=>item.id.toString())}.width('100%').alignItems(HorizontalAlign.Start)}}@Entry@Componentstruct SharedCartContextPage{@Provide('cartItems')cartItems:CartEntry[]=[]privateelectronics:CartEntry[]=[{id:1,name:'无线蓝牙耳机',price:299,qty:0,icon:'🎧'},{id:2,name:'便携充电宝',price:89,qty:0,icon:'🔋'},{id:3,name:'USB-C 数据线',price:29,qty:0,icon:'🔌'},]privatebooks:CartEntry[]=[{id:4,name:'HarmonyOS 开发实战',price:69,qty:0,icon:'📘'},{id:5,name:'ArkUI 组件详解',price:59,qty:0,icon:'📗'},]gettotalPrice():number{returnthis.cartItems.reduce((s:number,e:CartEntry)=>s+e.price*e.qty,0)}clearCart(){this.cartItems=[]}build(){Column({space:0}){// 顶部:导航栏(包含 CartBadge 深层消费数据)Row(){Column({space:2}){Text('@Provide / @Consume').fontSize(18).fontWeight(FontWeight.Bold).fontColor('#111827')Text('跨层数据共享,无需逐层传递').fontSize(12).fontColor('#6B7280')}.alignItems(HorizontalAlign.Start).layoutWeight(1)// CartBadge 是深层组件,通过 @Consume 直接获取购物车数据CartBadge()}.width('100%').padding({left:20,right:20,top:16,bottom:14}).backgroundColor(Color.White)// 说明Row(){Text('💡').fontSize(14).margin({right:6})Text('@Provide 在顶层注入数据;@Consume 在任意层级直接消费,无需 @Prop 层层传递').fontSize(12).fontColor('#1E40AF').layoutWeight(1)}.width('100%').padding({left:16,right:16,top:8,bottom:8}).backgroundColor('#DBEAFE').alignItems(VerticalAlign.Top)Scroll(){Column({space:16}){// 商品区域(ProductSection 是中间层,不持有状态)ProductSection({title:'📱 数码产品',items:this.electronics}).padding({left:20,right:20})ProductSection({title:'📚 技术书籍',items:this.books}).padding({left:20,right:20})// 购物车汇总if(this.cartItems.length>0){Column({space:10}){Row(){Text('🛒 购物车').fontSize(14).fontWeight(FontWeight.Bold).fontColor('#374151').layoutWeight(1)Text('清空').fontSize(13).fontColor('#EF4444').onClick(()=>this.clearCart())}.width('100%')ForEach(this.cartItems,(entry:CartEntry)=>{Row({space:10}){Text(entry.icon).fontSize(20)Text(entry.name).fontSize(13).fontColor('#374151').layoutWeight(1)Text('×'+entry.qty.toString()).fontSize(13).fontColor('#9CA3AF')Text('¥'+(entry.price*entry.qty).toFixed(2)).fontSize(13).fontWeight(FontWeight.Bold).fontColor('#EF4444')}.width('100%').alignItems(VerticalAlign.Center)},(entry:CartEntry)=>entry.id.toString())Divider().color('#E5E7EB')Row(){Text('合计').fontSize(14).fontColor('#374151').layoutWeight(1)Text('¥'+this.totalPrice.toFixed(2)).fontSize(20).fontWeight(FontWeight.Bold).fontColor('#EF4444')}.width('100%').alignItems(VerticalAlign.Center)}.width('100%').padding(16).backgroundColor('#FFF7ED').borderRadius(12).margin({left:20,right:20,bottom:24})}else{Text('购物车为空,去选购商品吧~').fontSize(13).fontColor('#9CA3AF').width('100%').textAlign(TextAlign.Center).padding({top:16,bottom:24})}}.padding({top:12})}.layoutWeight(1)}.width('100%').height('100%').backgroundColor('#F9FAFB')}}

写在最后

跨层购物车页 这个案例的价值,不在于用了多少组件,而在于它把@Provide 与 @Consume放进了一个完整页面里。照着这个思路改自己的业务,比单独背 API 更靠谱。

http://www.cnnetsun.cn/news/3525276.html

相关文章:

  • Apache Commons Collections 与Java Stream API:如何选择最佳数据处理方案
  • 067、3A联动与协同控制:曝光、对焦、白平衡的闭环优化
  • 千问里面的表格怎么复制?AI 导出鸭安卓版一键无损导出完整表格
  • 数字人导游商业化闭环模型(已跑通12家5A景区,ROI达1:5.8的3层变现架构与License授权陷阱预警)
  • 吃透这 17 个概念,比 95% 的开发者更懂 AI
  • 【2018-10-23】Linux下生成c/c++项目代码覆盖率的产生方法
  • 第3天:用C语言重写点灯程序
  • A Unified Representation Underlying the Judgment of Large Language Models
  • 内核模块变量定义与符号查找
  • Bateman量化回测全攻略:从历史数据到策略验证
  • iwck鼠标屏蔽终极指南:如何安全地在键盘上吃泡面而不误触
  • apple-signin-unity完全指南:从零开始集成苹果登录到Unity项目
  • Intern-S2-Preview-397B部署全攻略:LMDeploy、vLLM与SGLang性能对比
  • GripMock安全最佳实践:保护你的Mock服务器免受攻击
  • 第3周 AI 性能优化总结——从推理加速到服务架构的进阶路径
  • ChartQA任务新标杆:chartreader-0.8B-OptiQ-4bit的80题测试数据集与结果分析
  • 随机化防御如何评估?AutoAttack的EoT模式应用与结果解读
  • 如何利用 Kimodo-SOMA-SEED-v1.1 创建合成数据:完整工作流程
  • WVP-GB28181-Pro技术架构深度解析:构建企业级视频监控平台的技术实现方案
  • 别再被“智能体”营销话术忽悠!用12个真实故障日志反向推导AI Agent的本质定义(含OpenAI/Anthropic/Meta内部文档对比)
  • Replication Manager备份系统深度解析:Restic集成与S3存储
  • AI 一周大事盘点(2026 年 7 月 12 日~2026 年 7 月 19 日)
  • NomNom:终极《无人深空》存档编辑器,彻底释放你的游戏创造力
  • 使用AI编程开发鸿蒙应用:从环境搭建到实战示例
  • 为什么你的Kimi读不懂PDF?——从OCR质量阈值、元数据污染到嵌入式字体识别失败的底层归因分析
  • 网页内容提取准确率仅63%?Kimi最新v2.3.1分析引擎升级细节首次公开,含4项NLP模型参数调优策略
  • Akagi麻将AI助手:从菜鸟到高手的快速进阶指南
  • NoFences:零成本桌面分区革命,终结Windows图标混乱时代
  • iOS激活锁终极绕过方案:Applera1n工具完整操作指南
  • 终极ADB驱动一键安装方案:3分钟为Windows搭建完整Android开发环境