
文章目录共享状态只放真正共享的东西判断是否已加购很关键Provide 和 Consume 的使用边界页面结构怎么看完整代码写在最后购物车是最适合讲跨层状态的场景之一。顶部角标要知道购物车数量商品列表要能加购底部结算区还要算合计。如果每一层都手动传cartItems页面一复杂参数会传得很难看。这个 HarmonyOS7 案例用Provide和Consume把购物车状态放在祖先组件里后代组件按需消费。它解决的不是“少写两行代码”而是让跨层数据有一个稳定来源。共享状态只放真正共享的东西页面里的核心数据结构是CartEntry包含商品 id、名称、价格、数量和图标。这个结构会同时被角标、商品区和购物车区域使用所以适合放进跨层共享状态。但不是所有状态都应该这么做。比如某个按钮的按下态、某个局部弹窗的显隐就没必要放进Provide。共享状态越多后面越难判断谁改了它。判断是否已加购很关键isInCart()很短但它决定了商品按钮展示“加入购物车”还是“已加入”。这种判断建议单独封装不要在 UI 表达式里重复写。isInCart(id:number):boolean{returnthis.cartItems.some((e:CartEntry)e.idid)}配合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)se.qty,0)}build(){Stack({alignContent:Alignment.TopEnd}){Text().fontSize(26)if(this.totalCount0){Text(this.totalCount99?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:stringitems:CartEntry[][]isInCart(id:number):boolean{returnthis.cartItems.some((e:CartEntry)e.idid)}getQty(id:number):number{constfoundthis.cartItems.find((e:CartEntry)e.idid)returnfound?found.qty:0}addToCart(item:CartEntry){constidxthis.cartItems.findIndex((e:CartEntry)e.iditem.id)if(idx0){this.cartItemsthis.cartItems.map((e:CartEntry,i:number){if(iidx){return{id:e.id,name:e.name,price:e.price,qty:e.qty1,icon:e.icon}}returne})}else{item.qty1;this.cartItemsthis.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((){constidxthis.cartItems.findIndex((e:CartEntry)e.iditem.id)if(idx0){constnewQtythis.cartItems[idx].qty-1if(newQty0){this.cartItemsthis.cartItems.filter((e:CartEntry)e.id!item.id)}else{this.cartItemsthis.cartItems.map((e:CartEntry,i:number){if(iidx){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)}}EntryComponentstruct 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)se.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.length0){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 更靠谱。