news 2026/10/30 17:01:03

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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 更靠谱。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/30 16:58:07

067、3A联动与协同控制:曝光、对焦、白平衡的闭环优化

067、3A联动与协同控制:曝光、对焦、白平衡的闭环优化 一、一个让我熬夜三天的bug 2019年,某旗舰机项目,暗光场景下预览画面每隔几秒就“闪一下”——亮度突然跳变,色彩也跟着飘。测试部报了个P0级bug,说用户投诉“手机拍照像抽风”。 我带着团队排查了整整三天。第一天…

作者头像 李华
网站建设 2026/10/21 16:27:31

千问里面的表格怎么复制?AI 导出鸭安卓版一键无损导出完整表格

千问里面的表格怎么复制&#xff1f;AI导出鸭安卓版一键无损导出完整表格千问里面的表格怎么复制&#xff1f;AI导出鸭安卓版解决格式错乱与内容丢失千问表格复制难题全解&#xff1a;AI导出鸭安卓版技术实现与实测体验 一、引言&#xff1a;千问表格复制的普遍痛点与技术根源 …

作者头像 李华
网站建设 2026/10/21 16:27:23

数字人导游商业化闭环模型(已跑通12家5A景区,ROI达1:5.8的3层变现架构与License授权陷阱预警)

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;AI数字人虚拟导游教程 AI数字人虚拟导游正逐步成为文旅、展馆与线上教育场景中的核心交互载体。本章将带你从零构建一个具备语音驱动、表情同步与多轮对话能力的轻量级虚拟导游系统&#xff0c;基于开源框架…

作者头像 李华
网站建设 2026/10/21 16:28:40

吃透这 17 个概念,比 95% 的开发者更懂 AI

1. 机器学习机器学习是人工智能的核心分支&#xff0c;指让计算机通过数据自动学习规律和模式&#xff0c;而不需要显式编程。它分为监督学习、无监督学习和强化学习三大类。理解机器学习是进入 AI 世界的第一道门槛。2. 深度学习深度学习是机器学习的一个子集&#xff0c;基于…

作者头像 李华
网站建设 2026/10/16 3:06:36

【2018-10-23】Linux下生成c/c++项目代码覆盖率的产生方法

[历史归档] 本文原发布于 cstriker1407.info 个人博客&#xff0c;内容为历史存档&#xff0c;仅供参考。 发布时间&#xff1a; 2018-10-23 &#xff5c; 标题&#xff1a;Linux下生成c/c项目代码覆盖率的产生方法 &#xff5c; 分类&#xff1a; 编程 &#xff5c; 标签…

作者头像 李华