上篇讲完ArkUI-X跨端适配,后台问得最多的是:“本地Demo跑通了,怎么对接真实的后台数据?总不能一直用模拟的商品列表吧?” 之前我们的电商Demo所有数据都存在本地,重启App就清零,也不支持多用户下单。今天就用华为云Serverless服务实现端云一体化,不用自己买服务器、搭数据库,2小时把本地Demo变成能上线的线上应用。所有步骤适配API23,踩了8个官方文档没写的坑,最终实现商品数据云端存储、多用户共享、下单数据实时同步,赶稿可直接复用。
一、前言:为什么端云一体化是电商App的必选项?
之前的系列内容里,我们用AppStorage存临时数据、PersistentStorage做本地持久化、DistributedDataObject实现跨设备同步,但这些方案都有局限:
数据只存在用户本地,换个手机就丢失;
不支持多用户,A用户加购的商品B用户看不到;
无法做真实下单,所有交互都是模拟的。
端云一体化的核心价值就是用Serverless(无服务架构)把后端能力封装成API,前端直接调用,不用关心服务器运维、数据库扩容、安全防护这些复杂问题。对中小团队和个人开发者来说,能节省70%的后端开发成本,上线周期从1个月压缩到2天。
端云一体化能力对比(实测结论)
维度 | 纯本地存储 | 端云一体化(Serverless) |
|---|---|---|
数据持久化 | 仅本地,换设备丢失 | 云端永久存储,跨设备同步 |
多用户支持 | 不支持,数据隔离 | 支持,用户数据互不影响 |
运维成本 | 无 | 无(云厂商托管) |
开发难度 | 低(仅前端) | 低(前端+少量云函数) |
适用场景 | Demo演示 | 真实上线应用 |
二、核心概念辨析(官方没讲透的边界)
很多新手会把云存储和之前的分布式文件服务(DFS)搞混,先明确三个核心服务的定位:
2.1 云函数(Cloud Functions)
定位:运行在云端的代码片段,处理业务逻辑(比如下单校验、价格计算)。
类比:相当于后端的Controller层,前端通过HTTP请求调用,不用关心服务器部署。
适用场景:用户登录校验、下单逻辑处理、数据聚合计算。
2.2 云数据库(Cloud DB)
定位:云端NoSQL数据库,存储结构化数据(比如商品信息、订单记录)。
类比:相当于后端的Model层,比本地的
PersistentStorage容量更大、支持多用户访问。适用场景:商品列表存储、用户信息管理、订单数据持久化。
2.3 云存储(Cloud Storage)
定位:云端文件存储服务,存储非结构化数据(比如商品图片、用户头像)。
类比:相当于分布式的
DFS,但数据存在云端,所有用户可访问,支持CDN加速。适用场景:商品图片存储、视频资源托管、用户上传文件管理。
💡核心认知:云数据库存“数据记录”,云存储存“文件本身”,云函数处理“业务逻辑”,三者配合实现完整的后端能力。之前的DFS是跨设备共享本地文件,云存储是跨用户共享云端文件,这是两者的本质区别。
三、代码实现(API23 电商端云实战)
3.1 开通云服务(5分钟搞定)
登录华为云开发者联盟控制台,进入AppGallery Connect(AGC);
创建项目,选择HarmonyOS平台,开通云函数、云数据库、云存储三个服务;
在“我的项目-项目设置-常规”里复制应用APP ID,后续初始化SDK用。
3.2 初始化云SDK(UIAbility中完成)
在EntryAbility.ts中初始化云服务能力,确保所有页面都能调用:
import { abilityAccessCtrl, AbilityConstant, UIAbility, Want } from '@kit.AbilityKit' import { window } from '@kit.ArkUI' import { cloud } from '@hw-agconnect/cloud' export default class EntryAbility extends UIAbility { async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> { console.log('EntryAbility onCreate') // 1. 初始化云SDK(核心) try { // 从AGC下载的配置文件,放到entry/src/main/resources/rawfile目录下 const context = this.context await cloud.initialize(context, 'agconnect-services.json') console.log('云SDK初始化成功') } catch (err) { console.error('云SDK初始化失败:', err.message) } // 2. 后续初始化逻辑(权限申请、分布式服务等)... } // 其他生命周期方法省略... }3.3 封装云请求工具类(工程化必备)
创建entry/src/main/ets/common/CloudUtil.ets,统一封装云函数、云数据库的调用逻辑,避免重复代码:
import { cloud } from '@hw-agconnect/cloud' import { BusinessError } from '@kit.BasicServicesKit' /** * 云端服务工具类 * 统一封装云函数、云数据库的调用逻辑 */ export class CloudUtil { private static readonly DB_NAME = 'shop-db' // 云数据库名称 private static readonly GOODS_COLLECTION = 'goods' // 商品集合 private static readonly ORDER_COLLECTION = 'orders' // 订单集合 /** * 调用云函数 * @param funcName 云函数名称 * @param params 函数参数 * @returns 函数执行结果 */ static async callFunction(funcName: string, params?: Record<string, Object>): Promise<any> { try { const result = await cloud.callFunction({ name: funcName, params: params || {} }) console.log(`云函数${funcName}调用成功:`, result) return result.result } catch (err) { const e = err as BusinessError console.error(`云函数${funcName}调用失败:`, e.message) throw e } } /** * 查询云数据库 * @param collectionName 集合名称 * @param query 查询条件 * @returns 查询结果列表 */ static async queryDatabase(collectionName: string, query?: any): Promise<any[]> { try { const db = cloud.database({ name: this.DB_NAME }) const collection = db.collection(collectionName) const snapshot = await collection.query(query || {}).get() console.log(`云数据库${collectionName}查询成功,共${snapshot.length}条数据`) return snapshot } catch (err) { const e = err as BusinessError console.error(`云数据库${collectionName}查询失败:`, e.message) throw e } } /** * 插入云数据库 * @param collectionName 集合名称 * @param data 插入数据 * @returns 插入结果 */ static async insertDatabase(collectionName: string, data: any): Promise<any> { try { const db = cloud.database({ name: this.DB_NAME }) const collection = db.collection(collectionName) const result = await collection.add(data) console.log(`云数据库${collectionName}插入成功:`, result) return result } catch (err) { const e = err as BusinessError console.error(`云数据库${collectionName}插入失败:`, e.message) throw e } } }3.4 改造商品列表页(从云端加载数据)
修改之前的Index.ets,把本地模拟数据替换为云端查询:
import { router } from '@kit.ArkUI' import { CloudUtil } from '../common/CloudUtil' import { GoodsBean } from './GoodsItem' @Entry @Component struct Index { @State goodsList: GoodsBean[] = [] // 商品列表(从云端加载) @State isLoading: boolean = true // 加载状态 aboutToAppear(): void { // 页面显示时加载云端商品数据 this.loadGoodsFromCloud() } /** * 从云端加载商品列表 */ async loadGoodsFromCloud(): Promise<void> { this.isLoading = true try { // 调用云数据库查询商品列表,按价格升序排序 const result = await CloudUtil.queryDatabase('goods', { orderBy: { price: 'asc' } // 按价格升序 }) // 转换为本地GoodsBean对象 this.goodsList = result.map((item: any) => ({ id: item.id, title: item.title, price: item.price })) console.log('云端商品数据加载成功,共', this.goodsList.length, '条') } catch (err) { console.error('云端商品数据加载失败:', err) // 降级方案:加载本地模拟数据 this.goodsList = [ { id: 1, title: '【API23新特性】HarmonyOS 6.1 定制款商品', price: 5999 }, { id: 2, title: '华为Mate 60 Pro 先锋计划', price: 6999 } ] } finally { this.isLoading = false } } // 列表渲染逻辑(复用之前的LazyForEach代码,无需修改) build() { Column() { // 标题 Text('云端商品列表') .fontSize(24) .fontWeight(FontWeight.Bold) .margin({ top: 20, bottom: 20 }) // 加载状态 if (this.isLoading) { LoadingProgress() .width(60) .height(60) .margin({ top: 100 }) } else { // 商品列表(复用之前的LazyForEach代码) List() { LazyForEach(this.goodsList, (item: GoodsBean) => { ListItem() { GoodsItem({ goods: item }) } }, (item: GoodsBean) => item.id.toString()) } .width('100%') .layoutWeight(1) } } .width('100%') .height('100%') .backgroundColor('#f0f0f0') } }3.5 实现下单功能(云函数处理业务逻辑)
创建云函数:在AGC控制台创建名为
create-order的云函数,代码如下(Node.js环境):
const cloud = require('@hw-agconnect/cloud'); exports.handler = async (event, context) => { const db = cloud.database(); const orderCollection = db.collection('orders'); // 1. 校验参数 const { userId, goodsId, count } = event.body; if (!userId || !goodsId || !count) { return { code: 400, message: '参数缺失' }; } // 2. 查询商品价格 const goods = await db.collection('goods').doc(goodsId).get(); if (!goods) { return { code: 404, message: '商品不存在' }; } // 3. 计算总价 const totalPrice = goods.price * count; // 4. 插入订单数据 const order = { userId, goodsId, count, totalPrice, createTime: new Date().getTime(), status: 'pending' // 待支付 }; const result = await orderCollection.add(order); // 5. 返回结果 return { code: 200, message: '下单成功', data: { orderId: result.id, totalPrice } }; };在商品详情页调用下单云函数:修改
DetailPage.ets,点击“加入购物车”时调用云函数:
// DetailPage.ets 新增下单逻辑 async addToCart(): Promise<void> { if (this.goodsId <= 0) return try { // 模拟用户ID(实际项目中从登录状态获取) const userId = 'test-user-001' // 调用云函数创建订单 const result = await CloudUtil.callFunction('create-order', { userId: userId, goodsId: this.goodsId, count: 1 // 默认加购1件 }) if (result.code === 200) { promptAction.showToast({ message: `下单成功,订单号:${result.data.orderId}` }) router.back() } else { promptAction.showToast({ message: result.message }) } } catch (err) { console.error('下单失败:', err) promptAction.showToast({ message: '下单失败,请重试' }) } }四、踩坑记录(官方文档没写的8个细节)
云函数超时时间默认3秒:如果下单逻辑复杂(比如调用第三方支付),需要到AGC控制台把云函数的超时时间调到10秒以上,否则会报
timeout错误。云数据库查询默认返回20条:如果需要查询更多数据,要加
limit参数,比如query({ limit: 100 }),最多支持1000条。云存储跨域问题:如果在Web端访问云存储的图片,需要在AGC控制台配置跨域规则,允许你的域名访问,否则会报
CORS错误。模拟器调试云函数:本地可以用
agc-cloud-functions-emulator工具模拟云函数执行,不用每次都部署到云端,调试效率提升80%。云数据库权限配置:默认云数据库是私有的,需要配置安全规则,允许已登录用户读写,否则会报
permission denied错误。规则示例:allow read,write: if request.auth != null。云函数冷启动问题:长时间未调用的云函数会有冷启动延迟(1-3秒),可以在AGC控制台开启“定时触发”功能,每隔5分钟调用一次云函数,避免冷启动。
本地配置文件路径:
agconnect-services.json必须放到entry/src/main/resources/rawfile目录下,否则SDK初始化时会找不到配置文件。降级方案必须做:云端服务可能出现网络波动,所有云请求都要加
try-catch,失败时降级到本地模拟数据,保证App基本功能可用。