鸿蒙商城App评价模块实战:基于React Native for OpenHarmony的完整实现
发布时间:2026/10/2 3:38:19来源:尧图网络
做OpenHarmony商城App这个项目的时候我印象最深的不是首页里那些花哨的动效反而是“写评价”这个看起来平平无奇的功能。原因很简单它是用户下单之后最常碰到的操作入口同时牵扯到评分交互、文本输入、图片上传、网络异常兜底任何一环处理不好都会直接变成差评投诉。加上我们用的是React Native for OpenHarmony这套桥接方案很多在Android/iOS上跑得好好的逻辑到了鸿蒙上都得重新调一遍。今天就把这个实战模块从技术选型到最终落地的完整过程整理出来给正在做或打算做鸿蒙商城类App的团队一个参考。我把项目名定为rn_for_openharmony本质上是把一个原本跑在Android/iOS上的React Native商城业务通过OpenHarmony的RN适配层迁移到鸿蒙设备上。评价模块是其中一个Tab里的子页面但它几乎覆盖了RN开发中90%的典型场景本地状态管理、原生模块调用、图片权限处理、前后端联调、防重复提交。所以这篇文章不仅适合想了解RN在OpenHarmony上能不能用的朋友也适合那些已经在做鸿蒙App、想优化评价体验的开发者。1. 先说背景为什么写评价这个模块值得单独做一篇1.1 从用户和产品角度看评价模块评价模块对电商类App的价值远远不只是“给订单打分”这么简单。产品侧会拿评价内容做商品详情页的UGC填充运营侧会统计好评率、晒图率来调整商家策略连推荐系统都需要评价里的关键词来理解商品属性。换句话说评价模块的填写率和内容质量是直接影响后续业务转化率的核心指标。但从技术角度讲写评价又是一个异常集中的场景。用户可能在弱网下点提交可能在相册里挑了一堆大图可能输入到一半被系统杀了进程也可能连续快速点提交按钮导致重复入单。我见过不少项目在评价接口上出的生产事故大多是防重没做好或者图片没压缩导致请求超时。所以我在做这个模块的时候提前把异常路径当成第一优先级来设计而不是先把UI画出来再说。1.2 技术选型历程为什么选RN for OpenHarmony我们团队的现状是前端技术栈以React为主已有的商城业务在Android/iOS上就是React Native实现的沉淀了一批通用的业务组件和工具函数。如果鸿蒙端用ArkTS完全重写等于要维护两套互不相通的代码人力翻倍而且两个端的UI细节很容易出现微小偏差。当时对比了几条路ArkTS原生开发性能最好、系统能力调用最直接但开发周期长前端同学基本不能直接上手需要单独组建鸿蒙团队。Flutter for OpenHarmony跨端方案里渲染一致的体验很好但当时Dart侧的第三方库适配还不太全尤其是图片选择、上传这类需要调系统能力的场景得自己写Platform Channel。React Native for OpenHarmonyOpenHarmony SIG官方一直在推进react-native-openharmony适配层社区里也已经有不少常用组件完成了鸿蒙适配比如react-native-image-picker的ohos版本、异步存储、网络请求相关的库。对我们这种已有RN业务沉淀的团队来说迁移成本最低。最终选择RN for OpenHarmony核心原因是业务代码复用率高。我把原有商城项目里和平台强相关的代码剥离开之后评价模块大约90%的JS/TS代码可以原样保留主要改的是原生配置、权限声明、个别原生模块的调用方式。这个收益在人力紧张的情况下是非常划算的。当然这套方案也有代价RN在鸿蒙上的某些能力还比较新遇到问题能查到的资料不多基本得靠读源码和真机调试一点一点啃。后面第6部分我会把踩过的坑集中列出来。1.3 商城项目结构与评价入口整个商城App的模块划分大概是首页、分类、购物车、订单列表、订单详情、个人中心、写评价属于订单详情的二级入口。用户从“待评价”的订单卡片点进去订单详情页展示商品信息、价格明细、物流状态底部有一个“去评价”按钮。写评价页面需要拿到的基础数据主要有三类订单信息订单号、下单时间、店铺名。商品快照商品ID、标题、图片、规格。这里有个细节快照必须是下单时的数据而不是实时的商品信息否则用户评价时看到的价格和规格可能已经变了。用户信息用户ID、头像、昵称用于生成评价记录和匿名态控制。我在工程里定义了一个CommentTarget类型用来在页面跳转时携带这些数据interface CommentTarget { orderId: string; goodsId: string; goodsTitle: string; goodsImage: string; specText: string; shopName: string; }跳转时用路由参数传递如果参数缺失就默认展示空态并给出提示。这一步虽然简单但很影响体验有些机型低内存下页面重建后参数丢失需要支持用orderId重新拉取数据兜底。2. 工程基础RN for OpenHarmony部署与权限准备2.1 从模板工程起步如果你是第一次接触RN for OpenHarmony我最推荐的方式是直接用官方维护的模板工程初始化而不是自己手动搭。因为适配层的版本和React Native核心版本是绑定的手动pin版本很容易踩到依赖不一致的坑。初始化核心步骤大概是# 新建目录并初始化 mkdir rn_for_openharmony cd rn_for_openharmony # 使用ohos适配模板创建工程 npx react-native-ohos/cli init RNHarmonyDemo # 进入工程目录安装鸿蒙侧依赖 cd RNHarmonyDemo ohpm install开箱模板里已经包含了entry模块也就是鸿蒙侧的Native壳工程以及React Native侧的业务代码目录。业务代码部分会有一个index.ets或MainPage.tsx之类的入口在原生侧通过RNSurface或者自定义的RNOHCorePackage挂载到页面上。这里要提醒一点鸿蒙的构建系统是hvigor它和Android的Gradle完全是两回事。你在网上搜索问题时如果搜到的是Android Gradle的报错大概率不能直接套用。常见的一个报错是could not determine the dependencies of task :app:compiledebugjavawithjavac这是典型的Android工程写法和鸿蒙的entry模块没关系遇到类似的报错先检查是不是用错了构建工具。2.2 权限和隐私合规写评价这个页面涉及两个敏感系统能力访问相册和调用相机。此外网络请求是基础必备能力。在OpenHarmony里这些权限都需要在entry/src/main/module.json5里显式声明。我这里用到的权限对照表权限名类型用途备注ohos.permission.INTERNETsystem_grant网络请求默认授权ohos.permission.CAMERAuser_grant调用相机拍照需要动态申请ohos.permission.READ_MEDIAuser_grant读取相册图片需要动态申请ohos.permission.WRITE_MEDIAuser_grant保存图片到相册按需申请user_grant类型的权限不会在安装时自动授权必须在运行时通过Ability或者专门的原生模块主动请求。在RN层我封装了一个requestPermission的工具函数内部通过鸿蒙侧的abilityAccessCtrl申请权限然后通过回调把结果传回JS侧。另外一个很重要的点是隐私合规。国内做App都清楚应用首次启动时需要弹出隐私政策弹窗用户同意后才能申请敏感权限和初始化SDK。我们的做法是在原生入口的onWindowStageCreate阶段先判断是否已经同意隐私协议如果没有就弹一个原生Dialog用户点同意后写入本地标记再开始加载React Native的bundle。如果用户点拒绝就直接退出或停留在空白页避免在未授权的情况下触发任何网络请求。2.3 数据通信基本配置RN for OpenHarmony里JS侧和原生侧的通信走的是RN的标准Bridge机制NativeModules、NativeEventEmitter这些API在鸿蒙适配层都实现了。不过有些系统能力的API差异比较大比如图片选择原版的react-native-image-picker在鸿蒙上不能直接用得换成官方适配过的react-native-oh-tpl/react-native-image-picker。安装方式npm install react-native-oh-tpl/react-native-image-picker ohpm install react-native-oh-tpl/react-native-image-picker注意这里有个双依赖的概念因为RN库同时涉及JS侧和原生侧所以既要用npm装JS包装也要用ohpm装鸿蒙侧的Native实现。少装一个运行时就会报“TurboModule无法解析”之类的错误。网络请求我直接用的fetch底层会走到鸿蒙的HTTP能力。有一个比较隐蔽的坑是HTTP的证书校验策略、超时时间在鸿蒙上的默认值和Android不一样最好在原生侧配置一下连接超时否则弱网下请求会长时间挂起。3. 评价页设计与核心组件拆解3.1 页面结构与布局细节评价页整体采用了ScrollView包裹的结构因为内容可能很长尤其是图片一多小屏手机会溢出。页面的从上到下商品信息区商品缩略图 标题 规格整块区域用卡片样式包起来。评分区五颗星支持点击和滑动切换。内容输入区多行TextInput占位符提示“说说这件商品的使用感受吧”。图片上传区九宫格前N格是已选图片最后一格是加号按钮点击唤起选择器。匿名开关一个Switch组件控制评价是否匿名展示。提交按钮固定在页面底部避免用户往下滚动时还要找按钮。有一点很容易被忽略TextInput在ScrollView内部时快速滚动会触发输入框失焦导致键盘收起体验很割裂。我处理的办法是把keyboardShouldPersistTaps设置为handled让点击非输入区域时不在滚动过程中直接收起键盘而是先传递给子节点处理。3.2 手写星级评分组件评分组件看起来简单但我选择手写而不是直接找第三方库的原因有两个一是第三方库在鸿蒙上的触摸事件适配不稳定二是评分组件的交互逻辑很简单手写成本很低还可以严格控制星星的大小、颜色、间距。核心代码如下const StarRating ({ value, onChange }: { value: number; onChange: (v: number) void }) { const [score, setScore] useState(value || 0); const handlePress (star: number) { setScore(star); onChange(star); }; return ( View style{styles.starRow} {[1, 2, 3, 4, 5].map(star ( Text key{star} style{[ styles.star, { color: star score ? #FF9500 : #E5E5E5 } ]} onPress{() handlePress(star)} ★ /Text ))} /View ); };细节处有两个要注意星星的点击热区要适当放大。直接用Text时实际可点击区域可能只有文字本身那么一小块用户很容易点不中。我给每个星星外面包了一层TouchableOpacity并且用hitSlop扩大触控范围实测这个改动对提升评分操作成功率非常有帮助。状态更新要防抖。快速连续点击星星时如果每次都触发父组件的onChange会导致外层频繁setState重渲染起来星星会有闪烁感。我的做法是评分组件内部自己维护score只有最终选中后才回调父组件这样就算点了一串星星也不会引起无谓的全局重渲染。评分规则方面我们开放的是1到5的整数星没有做半星。因为主流电商的评价体系里半星反而会显得选项复杂用户决策成本变高。后端在接口里也限制了socre字段只能是整数校验逻辑前后端都做了一遍。3.3 内容输入与字数限制输入框用的是TextInput的multiline模式最大高度限制在120超过就内部滚动避免页面被拉得无限长。占位符文案是产品定的但我在实现时发现一个鸿蒙平台差异占位符颜色在某些系统版本上默认是浅灰色在浅色背景下几乎看不见所以需要显式设置placeholderTextColor。字数统计和最大限制这一块坑比较深。很多人直觉上觉得text.length就是字数但React Native的JavaScript引擎对中文字符统计是按UTF-16码元计算的一个emoji会被算成2个长度。我们产品规定最多500字如果直接用.length用户输入一串emoji时统计会提前爆掉体验相当差。我采用的统计方式const normalizeLength (text: string) Array.from(text).length; const handleTextChange (text: string) { const len normalizeLength(text); if (len 500) return; setContent(text); setRemaining(500 - len); };Array.from会把字符串按Unicode码点拆开一个emoji算1个一个中文也算1个这样基本符合用户对“字数”的感知。当然这也不是绝对严谨比如一些组合型emoji会被拆成多个码点但实际业务中已经够用了。如果后端有统一的字数校验规则前端统计方式最好跟后端对齐否则会出现在前端能提交、后端反而报错的情况。我还加了实时计数显示“还可以输入xxx字”当接近上限时数字变红提示用户即将超限。这块纯属用户体验细节但评价页面本身内容单一这种小小的状态提示能有效降低用户输入焦虑。3.4 匿名开关与商品信息复用匿名开关实现很简单一个Switch加一段说明文字“匿名评价对所有人不可见”。这个信息要拼到提交参数里。有一点要注意匿名不代表评分不可见只是昵称和头像不展示后端会单独用is_anonymous字段控制脱敏逻辑前端不要自己在界面上隐藏评分。商品信息区我把它封装成一个独立的GoodsCard组件传入CommentTarget对象渲染。做成独立组件而不是直接写在页面里的原因是订单详情页和评价页都要展示同一份商品快照复用组件能保证两端的UI表现和数据类型完全一致以后要改样式也只需要改一处。在性能优化上我给GoodsCard包了React.memo因为商品信息在评价页面内是不会变化的完全没必要随着输入框的内容变化而重新渲染。这一点在低端鸿蒙设备上效果非常明显尤其是打开键盘或滚动时整体帧率会更稳定。4. 图片选择、压缩与上传链路4.1 接入图片选择器图片选择是评价模块里原生依赖最重的一块。我在鸿蒙上用的是react-native-oh-tpl/react-native-image-picker调用方式跟原版API基本一致。选择相册图片import { launchImageLibrary } from react-native-oh-tpl/react-native-image-picker; const pickImages async () { const result await launchImageLibrary({ mediaType: photo, selectionLimit: 9 - selectedImages.length, }); if (result.assets) { const newImages result.assets.map(asset asset.uri); setSelectedImages(prev [...prev, ...newImages].slice(0, 9)); } };调用相机import { launchCamera } from react-native-oh-tpl/react-native-image-picker; const takePhoto async () { const result await launchCamera({ mediaType: photo, saveToAlbum: false, }); if (result.assets) { setSelectedImages(prev [...prev, result.assets[0].uri].slice(0, 9)); } };实际开发中有个比较棘手的问题鸿蒙相册返回的URI可能以file://或content://开头不同系统版本表现还不一样。如果直接拿这个URI去上传服务端可能收到一个无法识别的路径。我封装了一个resolveImagePath工具把URI转换成可以直接读取的本地文件路径转换逻辑里兼容了ph:、file://、content://几种前缀。这块没有通用的银弹最好的方法就是拿几台不同鸿蒙版本的设备多测几轮。4.2 压缩策略图片不压缩就直接上传是评价模块性能问题的第一大来源。现代手机拍出来的照片动不动就是3-8MB用户如果选满9张图一次提交可能产生几十MB的请求体弱网环境下基本必失败。我在项目里用react-native-compressor做压缩它的鸿蒙适配版同样带ohos前缀。压缩参数我调成const compressedUri await ImageCompressor.compress(uri, { quality: 0.7, maxWidth: 1280, maxHeight: 1280, output: jpg, });maxWidth和maxHeight都限制在1280是为了在清晰度和体积之间取一个平衡点。评价里展示的图片最大也就是详情页里一个四分之三屏宽的大图1280已经远超实际需求继续保留原图分辨率纯属浪费。实测一张5MB的照片压缩后基本在300-500KB左右九张图总量控制在4MB上下上传体验要好很多。这里有一个优化细节压缩是CPU密集型操作如果用户一次选了9张图不要同步循环压缩否则会造成明显的界面卡顿。我用的方案是限制并发数为3把压缩任务拆成批次每完成一个就更新进度提示避免压缩时页面像死掉一样。4.3 多图上传并发控制与进度反馈图片上传我用的是XMLHttpRequest而不是fetch原因是fetch在上传进度这一块支持很差而XHR可以监听upload.onprogress事件实时拿到上传百分比。RN的XMLHttpRequest实现里这个能力是可用的只是很多人没注意到。单个图片上传的核心代码const uploadSingleImage (uri: string, index: number) { const formData new FormData(); formData.append(file, { uri, name: comment_${Date.now()}_${index}.jpg, type: image/jpeg, } as any); return new Promisestring((resolve, reject) { const xhr new XMLHttpRequest(); xhr.open(POST, UPLOAD_URL); xhr.setRequestHeader(Authorization, Bearer ${token}); xhr.upload.onprogress (e) { if (e.lengthComputable) { const percent Math.round((e.loaded / e.total) * 100); onProgress?.(index, percent); } }; xhr.onload () { try { const data JSON.parse(xhr.responseText); if (xhr.status 200 data?.data?.picId) { resolve(data.data.picId); } else { reject(new Error(data?.message || 上传失败)); } } catch (err) { reject(err); } }; xhr.onerror () reject(new Error(网络异常)); xhr.send(formData); }); };多图的并发控制我封装了一个简单的任务调度器核心逻辑是始终只有3个上传任务在跑每完成一个就从待上传队列里拉一个补上。好处是避免并发过高打爆设备网络栈也让单张图片的进度回调不会在9张同时跑的时候乱作一团。上传成功后服务端返回的是图片IDpicId我把它放进一个images数组和短评文本一起用于提交评价接口。这样做有个好处图片上传和评价提交是两件事图片传失败了可以单独重试或删除不需要把整个页面状态全都推倒重来。5. 提交逻辑与异常兜底5.1 接口约定与参数封装评价提交接口的路径约定为POST /order/comment请求体是JSON格式{ orderId: 20250101001, goodsId: 100200300, score: 5, content: 商品很好发货也快, images: [picId_1, picId_2], isAnonymous: false }这里有几个接口设计层面的细节建议images字段在服务端接收时一定要区分“未传”和“传空数组”两种情况。未传代表前端没有图片能力传空数组代表用户一张图都没选。有些服务端框架对二者处理不当会导致默认值污染数据。content允许为空但前提是images不能为空数组也就是说晒图评价可以不写字。这个规则要写死在接口文档里前端校验时同步对齐。如果将来要支持追加评价接口里最好从一开始就带上commentType字段既能兼容首评也能在后续扩展追评场景避免改表结构。前端封装请求函数时我会统一注入token、traceId、渠道号这些公共参数并在网络层做一个错误码映射401跳登录、500弹服务端错误、超时弹网络提示。这样评价页面的业务代码就不需要到处写重复的try/catch了。5.2 前端校验校验规则我放在三个层面评分必须大于0。这个校验看起来多余但实际测试中我发现用户很容易只填文字忘了选星如果后端直接把score当成0处理这条评价就成了无效数据。内容和图片至少有一个非空。两者都为空时点击提交应该直接阻止并Toast提示“说点什么或传张图吧”。内容长度不超过500字图片数量不超过9张。这两个限制在前端做了强约束理论上不会超但为了防止手滑和极端情况提交前再校验一次总没错。校验不通过的提示方式我用的是RN的ToastAndroid在鸿蒙适配层也做了兼容。要注意Toast在“键盘弹起”状态下会被软键盘挡住提示要延迟到键盘收起之后或者干脆放在页面顶部用自定义横幅提示实测体验更好。5.3 防重复提交防重复提交是评价接口最重要的一道防线。用户在网络慢的场景下如果连续点击两次提交按钮就可能产生两条一模一样的评价处理起来非常麻烦。我的做法是三层防护按钮状态层提交中把按钮disabled并显示加载文案“提交中...”。这样用户第一次点击后按钮已经不可点了。标志位守卫用一个useRef变量保存isSubmitting在提交函数入口判断如果为true直接return避免异步回调里因为事件循环错乱导致重复执行。服务端幂等请求头里带一个clientRequestIdUUID服务端在同一订单号下根据这个ID去重。前两层只是前端体验兜底真正保证不重复入库的还是服务端这层幂等校验。实现代码片段const isSubmitting useRef(false); const handleSubmit async () { if (isSubmitting.current) return; isSubmitting.current true; setBtnLoading(true); try { await submitComment(payload); Toast.show(评价成功); navigation.goBack(); } catch (e) { // 恢复按钮状态保留已输入内容 setBtnLoading(false); // 弹窗让用户选择重试或保存草稿 } finally { isSubmitting.current false; } };一个容易被忽略的点是isSubmitting.current false要放在finally里而不是try里。如果请求抛异常后直接返回isSubmitting会一直是true用户就再也提交不了评价了这个bug非常隐蔽测试时如果没走异常分支很难发现。5.4 网络异常与草稿机制网络异常处理我做了两件事重试和草稿。重试比较简单用户点击重试只是重新走一遍handleSubmit流程之前填写的score、content、images都保留在页面状态里不存在丢数据的风险。草稿机制则更考验细节。用户在写评价的过程中可能突然锁屏、切后台、甚至App被杀。如果把输入内容丢了用户回来后心态崩溃大概率就不填了。所以我在onChangeText时做了一个防抖5秒内如果内容有变化就异步写入AsyncStoragekey用orderId goodsId做区分用户下次再进入这个页面时优先拉取本地草稿恢复内容提交成功后再清除对应草稿。草稿恢复到界面上时有一个小坑如果有图片是用本地路径临时存的草稿恢复时这些路径很可能已经失效比如App重启后临时目录被清掉。所以草稿里只恢复评分、文本、匿名开关图片不恢复并在界面上提示“草稿已恢复图片请重新选择”。这样至少保住了用户敲的字。6. 真机调试与踩坑记录6.1 启动白屏与bundle加载RN for OpenHarmony开发时最常见的现象就是启动白屏。调试模式下原生壳启动后会去连接Metro服务加载JS bundle这个过程中如果Metro没启动、端口被占用、或者局域网IP不通页面就一直白着。Android上如果Metro有问题会有红屏报错鸿蒙适配层早期版本对加载失败的处理不够完善往往什么都不显示。我排查白屏的固定步骤确认Metro服务已经启动端口默认8081。确认真机和电脑在同一个局域网内。在DevEco Studio的日志里搜“bundle”或“RNOH”相关关键字看有没有加载失败信息。如果测试包是release包确认bundle已经正确打包进assets里而不是仍然指向Metro。针对线上兜底我在原生侧做了一个启动超时机制RN Surface加载超过10秒无回调时展示一个内置的“加载失败请重启”页面而不是一直白屏干等。这个体验对真实用户很重要哪怕只是多一个错误反馈也能避免大量“打开App是空白”的负面反馈。6.2 键盘遮挡输入框评价页面输入框在页面中部键盘弹起时如果不处理输入框很容易被软键盘遮住。Android的adjustResize在这里有效但鸿蒙的窗口软键盘模式跟Android不完全一致。我一开始直接用KeyboardAvoidingView设置behaviorheight在部分鸿蒙版本上表现正常但在另一部分版本上完全没反应。后来我换成了手动监听键盘事件const [keyboardHeight, setKeyboardHeight] useState(0); useEffect(() { const subShow Keyboard.addListener(keyboardDidShow, e { setKeyboardHeight(e.endCoordinates.height); }); const subHide Keyboard.addListener(keyboardDidHide, () { setKeyboardHeight(0); }); return () { subShow.remove(); subHide.remove(); }; }, []);拿到键盘高度后给ScrollView的contentContainerStyle动态加一个paddingBottom。这个方案比KeyboardAvoidingView稳定得多而且可以精确控制滚动位置让用户输入时光标所在区域自动滚到键盘上方。这个“获取高度手动padding”的思路在RN跨端里通用以后遇到类似问题也能复用。6.3 依赖冲突与构建失败鸿蒙工程的依赖管理同时涉及npm和ohpm两套体系这也是最容易出问题的地方。比如react-native-image-picker的npm包和ohpm原生包版本不一致hvigor构建时会直接报找不到模块。我在第2部分提到过双依赖安装的逻辑。实际中光有ohpm包还不够还需要在entry模块里对原生包进行注册如果不注册运行时报错是“table unknown”或者“module not found”。具体做法是在工程的原生入口文件里把对应的TurboModule或Package实例注册进RN宿主。另外有个非常容易踩的坑npm包和ohpm包的版本号之间可能不是一一对应需要看仓库文档里的兼容对照表。我遇到过一次npm装的是2.0.0但ohos原生包只支持1.9.x导致运行时某个方法报“undefined is not a function”。排查半天最后还是把npm版本降级才解决。所以遇到奇怪标红第一步不是改代码而是检查依赖版本匹配度。6.4 性能与渲染细节评价页面在低端鸿蒙设备上的性能问题主要集中在这几处评分组件重渲染如果评分组件的onChange冒泡到页面顶层并经setState触发全页面重建输入框就会明显卡顿。我把评分组件内部状态独立持有只在最终确认时回调有效地把渲染范围限制在组件内部。图片列表已选图片的九宫格我用FlatList自带的numColumns渲染而不是直接map。图片多的时候只有可视区域内的item会被渲染滑动体验顺滑很多。提交按钮的loading态提交过程会有压图片、传图片、提交评价多个阶段每个阶段耗时都不同。我给按钮展示的是“X/N”进度比例虽然实现稍微繁琐一点但用户能明确知道当前卡在上传还是提交不容易产生“点了没反应”的错觉。另外ScrollViewTextInput的组合在鸿蒙上还有一个比较微妙的问题如果页面里放了很多图片缩略图快速滑动时可能会因为图片解码排队导致掉帧。我建议图片的缩略图尺寸控制在200px左右不要直接渲染原图的比例哪怕压缩后的图片是1280宽缩略图也单独生成一份。这一点在图片数量多的时候优化效果尤其明显。写评价这个功能的代码量在整包App里占不了多大比例但它的工程质量直接体现出一个团队对用户体验的用心程度。我个人最大的体会是别因为页面简单就跳过异常处理输入内容丢失、重复提交、图片上传失败这些才是真正会引发用户流失的问题。在RN for OpenHarmony这种新生态上做开发依赖版本和构建配置的坑会掩盖很多业务逻辑本身的问题所以写功能前先把工程基础打扎实后面会省钱得多。最后再分享一个小技巧评价模块的图片上传和内容提交我建议从一开始就拆成两个独立接口。虽然这会增加一次网络请求但换来的是“图片可重试、内容可草稿、状态可恢复”的灵活性。如果图省事把图片和文本绑成一个提交接口一旦失联通杀用户填的几百字就全没了。就冲这一点我觉得拆开是最值的决定。
网站建设高端定制企业官网