ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

RNOH跨端适配实战:商城个人资料编辑模块鸿蒙化改造全复盘

RNOH跨端适配实战:商城个人资料编辑模块鸿蒙化改造全复盘 作为一个在移动端折腾了好几年的开发者我最近把React NativeRN那套代码跑到了OpenHarmony设备上做了一个商城项目的实战改造。这个过程中个人资料编辑这个看似简单、实际上处处是坑的模块耗费了我大量精力去适配和调优。今天就把这段真实经历拆开揉碎了分享出来尤其是React Native for OpenHarmony后面简称RNOH下那些和原生RN不一样的地方给正打算在鸿蒙生态里复用自己的RN代码库的兄弟们一个参考。这个模块虽然只是商城App里的一个子功能但它涵盖了表单校验、图像选择与上传、多端适配、键盘交互、状态同步等几乎所有的移动端基础能力。无论是你对RNOH的跨端能力心里没底还是已经在鸿蒙设备上跑RN但遇到了一堆适配问题这篇实战复盘应该都能帮到你。1. 项目背景与整体设计思路1.1 为什么选择React Native for OpenHarmony说实话一开始接到“把商城App移植到鸿蒙设备”这个需求时我的第一反应是直接用ArkUI重写。但看了下时间排期再看看手头那一大坨已经在多个业务线跑了好几年的RN业务代码重写显然不现实——光是商品详情页里的那几十个自定义组件就够我们喝一壶的。所以最终方案很明确用RNOH方案把现有RN代码直接跑在OpenHarmony系统上。RNOH相当于在鸿蒙系统上实现了一个React Native的运行时和渲染引擎的适配层它让你写的JS/TS逻辑可以复用虽然UI最终是通过鸿蒙的原生组件去渲染的但对你来说写代码的体验和标准RN几乎没差。个人资料编辑模块正好是一个完美的“试金石”项目。它不涉及太复杂的原生SDK但又高度依赖系统的输入法、相册、城市选择等能力跨端适配的典型问题在这个模块里基本都能碰到。拿它来检验RNOH的成熟度再合适不过。1.2 个人资料模块在商城App中的定位商城App里的个人资料编辑用户能直接看到的入口就是“我的-设置-个人资料”。但在这个页面背后我拆解出来的核心任务其实有四个基础信息展示与编辑头像、昵称、性别、生日、个人签名、常驻城市等字段。这些字段在UI上看着都挺简单但每个字段的存储格式和校验逻辑都不一样比如生日需要日期选择器城市需要省市区级联签名需要实时统计字数。头像上传链路从相册选图、裁剪到上传到对象存储再到回显。这条链路是用户感知最强的部分也是出错率最高的部分。表单页的交互体验页面进入时要能回显已有数据编辑过程中要有即时的输入体验和校验反馈保存时要能处理多接口并发的loading状态。实时同步机制资料修改成功后要第一时间同步回其他页面比如“我的”页面里的头像和昵称不然用户会觉得App数据不同步、有bug。从技术拆分的角度来看这四个任务分别对应了数据建模、原生能力桥接、表单状态管理和全局状态同步。这些点解决好这个模块基本就完工了。1.3 技术选型与整体架构取舍在这个模块里我做的核心技术决策有三个每一个都是经过对比踩坑之后才确定的第一状态管理没有引入Redux或MobX而是用了React Context useReducer。原因很简单个人资料相关的全局状态其实只有“当前用户信息”这一份用Context就能满足跨页面同步的需求引入Redux反而会让这块本不复杂的逻辑变得繁琐。如果你整个项目已经重度依赖Redux那另说但如果只是这个模块Context完全够了。第二网络请求层直接axios上传走单独的fetch方法。头像上传和普通的JSON接口差异很大需要multipart/form-data格式而且可能需要单独配置超时时间。分开处理能有效避免上传大图时把正常接口的请求线程也拖垮。第三城市选择没有用第三方的React Native Picker库而是走了自绘的弹层 系统滚动选择器。RNOH生态下第三方库的兼容性参差不齐很多原生依赖的库在鸿蒙上并没有对应的实现与其去改库源码不如自己封装一层至少出了问题你能看得懂、改得动。2. 用户中心与资料编辑核心功能拆解2.1 个人信息字段建模与数据存储设计个人资料的数据模型是整个模块的地基。我在设计时把用户信息分成了三块这块的规划直接影响后端的接口设计和前端的页面结构。第一块是展示型字段比如头像URL、昵称、性别、生日。这类字段的特点是非结构性、展示频率高直接存在用户主表里就行。第二块是描述型字段比如个人签名、常驻城市这类字段一般是后来加的需求建议单独存扩展表或者JSON字段避免频繁改表结构。第三块是敏感字段比如手机号、邮箱。这部分数据在资料编辑页里通常只展示、不允许直接编辑修改它们往往需要走单独的验证流程。比如改手机号需要短信验证码改邮箱需要邮件验证。在商城这种实名属性比较强的App里这块的合规性尤其重要我建议前端在展示这些字段时做脱敏处理比如手机号只显示前三位和后四位。来看一下我当时设计的TypeScript类型定义你可以直接参考// 用户资料基础类型定义 export interface UserProfile { userId: string; // 基础资料 avatarUrl: string; nickname: string; gender: male | female | unknown; birthday?: string; // 格式YYYY-MM-DD signature?: string; // 个人签名最多120字 city?: string; // 存储格式北京市-北京市-朝阳区 // 脱敏展示字段 mobile?: string; // 已脱敏例如 138****8888 email?: string; // 已脱敏 // 服务端返回的资料完整性标记 profileComplete: boolean; }这里有个细节得提醒你城市字段的存储格式要在最早就和前后端对齐。我见过很多项目把城市字段存成一个纯字符串结果前端要回显省市区的时候还得从字符串里解析又容易出错又麻烦。如果你有独立的城市表那后端直接返回省市区三个ID是最理想的如果没有那就在前端约定好格式比如“北京市-北京市-朝阳区”用连字符隔开方便前端拆分回显。2.2 资料编辑页面状态管理与跨页面同步策略个人资料页的状态管理是一个典型的“局部复杂全局简单”场景。页面内部每个字段都有自己的编辑态、校验态、脏标记就是判断哪个字段被改过页面外部只有“保存成功”后才需要通知别人。关于脏标记我强烈建议你要做。原因很现实用户进到编辑页可能只改了头像就点保存如果前端不区分哪些字段被改过最后提交的时候就会把其他字段也提交一遍这不仅造成无意义的接口调用在某些场景下还会把用户老的数据给覆盖成空值。我就见过有项目在提交时把没填的生日字段直接传了空字符串结果后台老数据被清掉的惨案。状态管理我这里用useReducer实现比多个useState堆在一起清晰得多type ProfileState { profile: UserProfile | null; loading: boolean; saving: boolean; dirtyFields: Setstring; }; type ProfileAction | { type: LOAD_SUCCESS; payload: UserProfile } | { type: UPDATE_FIELD; field: keyof UserProfile; value: string } | { type: SAVE_START } | { type: SAVE_SUCCESS } | { type: SAVE_ERROR }; function profileReducer(state: ProfileState, action: ProfileAction): ProfileState { switch (action.type) { case LOAD_SUCCESS: return { ...state, profile: action.payload, loading: false }; case UPDATE_FIELD: { const dirtyFields new Set(state.dirtyFields); dirtyFields.add(action.field); return { ...state, profile: state.profile ? { ...state.profile, [action.field]: action.value } : state, dirtyFields, }; } case SAVE_START: return { ...state, saving: true }; case SAVE_SUCCESS: return { ...state, saving: false, dirtyFields: new Set() }; case SAVE_ERROR: return { ...state, saving: false }; default: return state; } }跨页面同步我用了一个UserContext在“我的”页面和“个人资料”页面之间共享。保存成功后直接调用更新Context里的用户信息这样返回“我的”页面时头像和昵称自然就刷新了。这个方法相比EventBus或全局广播的优点是数据流是单向的多个页面之间不会互相污染状态。2.3 头像选择、上传与回显的完整链路头像上传是整个资料模块里原生依赖最深、坑最多的地方。RNOH环境下很多在iOS/Android上跑得好好的图片选择库没法直接用因为背后的原生代码依赖了UIKit或Android的Activity机制。我在实际项目里走了以下方案第一步图片选择。我们最终没有依赖react-native-image-picker这个库而是通过RNOH的自有桥接能力封装了一层原生ArkTS代码直接调用鸿蒙系统的PhotoViewPicker来选图。你只需要在原生侧写好一个Module暴露一个selectImage方法给JS侧调用返回值是图片的URI和临时文件路径。这一块的实现逻辑不复杂但要注意时区、权限等问题。第二步图片裁剪与压缩。老实说裁剪功能在RNOH生态里能做得很深的不多。我的做法比较务实前端拿到图片后通过react-native-image-resizer如果兼容的话或者直接在原生侧完成压缩。如果你们没有裁剪需求那直接在选图后走压缩就完事了。尺寸我这边统一压到512x512以内质量80%一张约5MB的图片能压到200KB左右上传体验好很多。第三步上传。这里我必须说一个经验不要用普通的axios实例去传图片新建一个专门的上传函数并且把超时时间调到60秒以上。另外上传前一定要取得文件的真实路径RNOH下有可能会给你返回一个file://协议格式的路径你要确认这个路径能被fetch读取到。下面的代码是上传的核心逻辑你可以放在自己的工具类里/** * 上传图片到对象存储 * param fileUri 本地图片URI * param uploadUrl 服务端上传地址 */ export async function uploadAvatar(fileUri: string, uploadUrl: string) { // 注意这里需要将 file:// 路径转为FormData可用的对象 const fileObj { uri: fileUri, type: image/jpeg, name: avatar_${Date.now()}.jpg, }; const formData new FormData(); formData.append(file, fileObj as any); // 单独的上传请求超时时间调长 const response await fetch(uploadUrl, { method: POST, body: formData, headers: { Content-Type: multipart/form-data, // 这里带上你们的鉴权token Authorization: Bearer ${global.authToken}, }, timeout: 60000, }); const result await response.json(); return result.data.url; // 返回可访问的URL }上传完成后返回的URL不要直接拿来做图片回显一定要在回调里再把新的URL更新到用户列表数据里这样“我的”页面才会同步刷新。如果你还想做本地的图片缓存可以用react-native-fast-image如果RNOH有对应实现或者简单的Image组件配合URL来渲染问题不大。3. 实操过程从零搭建个人资料编辑模块3.1 资料编辑页面的前端布局与交互设计先讲布局。个人资料编辑页在UI结构上其实是很经典的“表单页”顶部是导航栏中间是分组的表单项列表底部是保存按钮。在RN里实现时我推荐用SectionList来承载整个页面而不是用ScrollView套多个View。原因有二一是SectionList支持分组和粘性头部后续如果还要加“账号安全”这一类分组项会更方便二是长列表在RN里的性能表现比ScrollView好得多当字段多起来时滚动的流畅度差异非常明显。分组设计上我分了三组基础资料头像、昵称、性别、生日个人介绍个性签名、常驻城市账号信息手机号、邮箱只读每一项用ProfileRow组件来封装右侧是当前值点击进入编辑态。这样页面的代码结构相当清晰可维护性很高。这里放一下页面主体的核心代码结构const ProfileEditScreen () { const { state, dispatch } useProfileReducer(); const { profile, saving } state; const renderRow ({ item, index }) { return ( ProfileRow key{item.key} label{item.label} value{item.value} onPress{item.onPress} showArrow / ); }; const sections useMemo(() [ { title: 基础资料, data: [ { key: avatar, label: 头像, value: profile?.avatarUrl }, { key: nickname, label: 昵称, value: profile?.nickname }, { key: gender, label: 性别, value: getGenderText(profile?.gender) }, { key: birthday, label: 生日, value: profile?.birthday }, ], }, { title: 个人介绍, data: [ { key: signature, label: 个性签名, value: profile?.signature }, { key: city, label: 常驻城市, value: profile?.city }, ], }, { title: 账号信息, data: [ { key: mobile, label: 手机号, value: profile?.mobile, disabled: true }, { key: email, label: 邮箱, value: profile?.email, disabled: true }, ], }, ], [profile]); return ( View style{styles.container} SectionList sections{sections} renderItem{renderRow} keyExtractor{(item) item.key} stickySectionHeadersEnabled{true} / Button title保存 loading{saving} onPress{handleSave} disabled{state.dirtyFields.size 0} / /View ); };注意底部保存按钮的状态初始化时是置灰的只有当dirtyFields不为空时才可点击。这个交互细节能给用户很强的心理暗示——我不会让你做无意义的保存。3.2 表单字段编辑与校验实现详解表单编辑是这个模块的重头戏。我把不同字段的编辑方式进行了分类文本类字段昵称、个性签名这类字段我用一个Modal底部弹出层 内嵌TextInput实现而不是跳转到单独的新页面。原因在于跳转页面的路由开销较大而且底部弹出层的编辑体验更接近目前主流电商App的操作习惯。在昵称输入时需要限制长度比如昵称20字符、签名120字符并且实时显示字数统计。为应对RNOH的输入法问题这里有几个非常关键的配置不做的话用户会在输入时疯掉TextInput value{nickname} onChangeText{handleNicknameChange} placeholder请输入昵称 maxLength{20} returnKeyTypedone onSubmitEditing{handleConfirm} // 关键关闭自动大写移动端输入昵称很少有人需要首字母大写 autoCapitalizenone // 关键关闭拼写检查 autoCorrect{false} // 关键清除按钮iOS上比较常用 clearButtonModewhile-editing // 关键限制单行 multiline{false} /对于签名是multiline{true}这时需要额外处理文本高度自适应的问题。我建议设置一个最小高度当内容增多时动态增高不要让它固定死。性别与生日的选择性别我直接用了一个交互组件来做底部弹出选择器里面是三个选项男、女、保密。生日则是用了react-native-community/datetimepicker如果它在RNOH下能正常工作或者退而求其次自绘三个滚轮年、月、日。我实际项目里是自绘了滚轮虽然代码量多一些但完全可控不会因为RNOH下某个原生模块的适配问题而卡住整个页面。城市选择这个相对复杂因为涉及到省-市-区三级联动。产品上一开始要求只选到城市后来运营又改成要选到区还好我用了组件化的选法没有把逻辑写死在页面上这也是前端的家常便饭——需求总会变的。城市数据的来源建议打包一份JSON静态数据放在前端避免每次进入页面都要请求城市列表对用户体验是很大的提升。3.3 保存逻辑与接口联调细节保存的时候前端要做的事情不只是把dirtyFields里的字段传给后端那么简单。我的保存函数是这样设计的先校验所有编辑过的字段不通过直接提示不发起请求。如果有头像变更先走上传接口拿到URL更新到本地状态。把dirtyFields里的字段按后端需要的格式组装剔除未修改的字段。请求保存接口设置合理的超时和重试策略。保存成功后更新全局Context最后关闭页面。这个流程里最容易出问题的是第二步和第三步的顺序。一定要先上传头像再提交整个资料表单。如果你同时提交后端要处理图片上传和字段更新两个事情事务性很难保证。而且从用户体验的角度资料保存失败的情况下已经上传成功的图片就成了“孤儿文件”除非你后续做清理机制否则白白占了存储空间。关于接口请求我个人建议统一封装一下错误码。服务端在字段校验失败时返回的错误格式可能五花八门前端最好做成“错误码 - 用户提示”的映射比如10001代表“昵称已被占用”直接给用户展示“该昵称已被占用换个试试吧”。3.4 多端适配与键盘避让优化个人资料编辑页是高度依赖键盘输入的页面所以在OpenHarmony设备上键盘的弹出和收回、键盘对输入框的遮挡是我调试得最多的部分。首先状态栏和导航栏的高度适配不能用iOS那套硬编码逻辑。RNOH环境下建议使用StatusBar.currentHeight来动态获取状态栏高度并使用react-native-safe-area-context的SafeAreaView包一层确保顶部和底部的安全区域内布局不出错。鸿蒙设备的屏幕比例非常多有平板、有折叠屏不用安全区组件的话基本必出问题。其次是键盘避让。KeyboardAvoidingView是RN自带的组件但在RNOH上它的表现和原生RN不太一样我在实际测试中发现有时候避让高度计算不准确导致键盘弹起来后输入框还是被挡住。我的解决方案是弃用KeyboardAvoidingView改成监听键盘事件 手动滚动。做法也不复杂在页面注册Keyboard.addListener(keyboardDidShow, handler)拿到键盘高度后用scrollTo方法把当前聚焦的输入框滚动到键盘上方。由于键盘弹出动画时长通常在200-300ms监听事件时做一下延迟滚动效果非常接近原生。这里还有一个细节Android以及鸿蒙的windowSoftInputMode设置会影响键盘弹起方式。如果原生侧没有设置adjustResize键盘弹出时页面高度不会变化JS侧拿到的键盘高度可能是0。我们是在原生工程里设置了adjustResize这样键盘弹出时页面会被压缩再配合手动滚动就非常丝滑了。4. 常见问题与排查技巧实录4.1 问题一页面白屏但不报错React Native启动白屏经典坑这个模块开发到一半时我遇到过最诡异的问题在模拟器上打开个人资料页有时候会白屏几秒才出来。那个状态下页面没有任何崩溃日志也没有红色报错屏幕就像JS线程卡死了一样。排查思路分了三步先判断是慢还是死给页面入口加了一个performance.now()的日志确认从页面挂载到渲染完成的时间差。再排查是JS侧还是原生侧的问题把页面内的业务组件逐个注释二分定位到是SectionList还是某个子组件导致的。最后定位到是头像加载组件在渲染大图时阻塞了RN的UI线程。这里的关键点在于RNOH环境下的原生图片加载是异步的但如果同时有大量的网络图片请求并发发出会占用系统资源导致JS线程的某些操作被延迟。解决方式是给Image组件加懒加载策略只渲染出现在视口内onViewableItemsChanged可以判断的图片同时为头像列表加上本地缓存。实际上“启动白屏”这个词在RNOH上比原生RN更敏感。RNOH环境下每次加载JSBundle的时间都要比原生RN长如果你的包本身就有几MB再加上桥接初始化白屏就会格外明显。优化思路是减少首页JSBundle的体积开启分包加载不要让资料编辑页的代码在启动时就全部加载。如果你们项目用Hermes引擎确认RNOH是否支持支持的版本就开启内存优化。做一个原生的启动占位页在JS加载完成前给用户足够的视觉反馈避免白屏带来的“死机”错觉。4.2 问题二调用原生图片选择器时没有反应在接入鸿蒙原生图片选择能力时我先是遇到了点击按钮完全没反应的问题。查了很久排除了权限排除了方法名拼写错误最后发现是原生Module的导出方法没有加上Method装饰器。在RNOH原生封装中暴露给JS层的方法必须用Method标记否则就算你在JS侧写了调用代码也不会出发任何回调。这算是一个RNOH和原生RN差异比较大的地方。写原生桥接代码时务必检查你的ArkTS方法是否都加了Method。另外一个常见的坑是图片选择返回的URI不能直接用于fetch上传需要通过原生代码把URI转换成文件路径或者把图片拷贝到应用的缓存目录下。如果你的URI是基于file://协议的大多数场景没问题但如果是content://或者鸿蒙自己的datashare://协议直接拿来用大概率报错。处理方式是在原生侧写一个统一的转换方法Method async getRealPathFromUri(uri: string): Promisestring { // 将 URI 转为文件路径并返回 // 不同平台协议不同建议用系统API处理 }4.3 问题三软键盘弹起后页面整体被顶上去这个问题的表现是在编辑昵称时点击TextInput整个页面包括顶部的导航栏都被键盘顶了上去看起来非常奇怪。在原生RN中这通常是KeyboardAvoidingView的behavior设置问题但在RNOH下很多时候是因为原生侧的windowSoftInputMode配置不对。我排查后确定问题出在AndroidManifestOpenHarmony工程配置里的windowSoftInputMode没有设置为adjustResize而是用了默认值。在RNOH环境中页面都是基于原生Window渲染的如果不允许窗口调整大小键盘弹出时整个Activity的窗口就被强制往上顶了。把配置改成adjustResize后只有页面底部内容被压缩导航栏保持固定问题就解决了。补充一个细节在鸿蒙真机上部分虚拟键盘是“悬浮键盘”模式并不会压缩窗口而是直接悬浮在应用上方。这种情况JS侧无法通过键盘事件拿到准确的高度所以如果真的遇到悬浮键盘下的遮挡问题只能引导用户关闭悬浮键盘模式或者用自定义的编辑区域比如底部固定的评论框来规避。4.4 问题四头像上传成功但图片不刷新这个问题特别经典。上传接口返回了新的URL更新了状态但是界面上的头像就是不动。查找之后发现是图片缓存机制搞的鬼。RN的Image组件默认会对同一URL做缓存这次拿到的新URL理论上不会命中缓存但我用的react-native-fast-image如果有对应RNOH实现对URL做了基于路径的缓存当URL的目录结构不变、只是文件名变化时它有概率命中原有缓存。这个问题的终极解法是给图片URL加一个版本号参数const getAvatarUrl (url: string) { // 添加时间戳避免缓存 return ${url}?v${Date.now()}; }; // 使用时 Image source{{ uri: getAvatarUrl(profile.avatarUrl) }} /虽然不太优雅但在没有更好的缓存清理机制的情况下这是最稳妥、见效最快的方案。4.5 常见问题速查表问题表现可能原因解决方案页面白屏且无报错JSBundle加载慢/图片加载阻塞分包加载、图片懒加载、原生占位图点击按钮调用原生方法无反应原生Module方法未加Method装饰器检查原生侧方法装饰器是否齐全键盘弹起页面被顶上去windowSoftInputMode配置不对改为adjustResize图片上传后界面不刷新图片缓存命中URL加时间戳参数TextInput输入卡顿键盘弹出动画与JS线程冲突监听键盘事件延迟滚动相册选图后无法上传URI协议不被fetch支持通过原生方法转为文件路径保存时字段被重置为空脏标记未正确维护使用Set管理dirtyFields提交时过滤5. 性能优化与体验细节打磨5.1 减少无效渲染与模块级性能优化个人资料页的字段不多但每个字段的变更都会触发ProfileEditScreen重新渲染。如果整个页面一次性渲染几十个ProfileRow在低端鸿蒙设备上还是能感觉到明显卡顿的。我的优化手段是把每一行ProfileRow用React.memo包裹。这样当某个字段值变更时其他行的props没有变化React就不会重新渲染它们。这个改动一行代码的事但对列表页的渲染性能提升非常明显。还有一个点头像上传进度的反馈。上传大图时一定要给用户一个进度条。RNOH环境下fetch不支持上传进度回调所以如果你需要进度反馈还是得走原生桥接把上传进度通过事件DeviceEventEmitter回传给JS侧。如果你嫌麻烦也可以做一个假的loading动画至少保证用户知道“在上传中”而不是以为卡死了。5.2 用户输入防抖与保存按钮状态机的细节昵称或者签名的输入如果每敲一个字就触发一次校验既浪费资源也会导致在低端设备上输入过程有明显的掉帧。我这边做了一层300ms的防抖处理。这个处理对输入类字段都适用用户体验反而更好——等到用户停顿了再校验是符合直觉的。防抖代码很简单import { useRef } from react; function useDebouncedCallbackT extends (...args: any[]) void( callback: T, delay: number 300 ) { const timer useRefReturnTypetypeof setTimeout(); return (...args: ParametersT) { if (timer.current) clearTimeout(timer.current); timer.current setTimeout(() callback(...args), delay); }; }保存按钮的状态机也做了处理分为五种状态disabled无修改、idle可点击、saving保存中、success保存成功、error保存失败。每种状态对应不同的文案和颜色。特别说一下success状态在接口保存成功后按钮文案会变成“已保存”1.5秒后自动恢复同时返回上一页。这个小细节让用户明确感知到“我的操作生效了”对信任感的提升挺大的。5.3 弱网环境下的处理策略商城App的用户什么网络环境都有弱网下编辑资料的体验如果做不好用户分分钟卸载。我在这个模块里做了两个策略第一所有获取资料的请求都做了本地缓存。进入页面时先展示缓存数据再静默拉取最新数据这样即使网络很慢用户也不会看到空页面。缓存用AsyncStorage存一份JSON设置合理的失效时间比如10分钟。这个设计在商城App里尤为重要因为用户经常会在地铁、电梯等弱网环境下进入“我的”页面。第二提交保存失败时不丢数据。如果用户填了一堆信息保存时因为网络问题失败了重新进入页面发现所有内容都被清空了这绝对是劝退级体验。因此我在保存失败时保留了当前页面所有状态只弹出一个toast提示“网络异常请重试”用户点击保存按钮会再次发起请求。这样的容错策略可能在测试阶段看不出价值但上线之后一定能挽回不少用户口碑。最后的实操体会做这个RNOH商城项目的个人资料编辑模块前后花了两周多的时间其中最耗时的不是写业务的逻辑而是排查各种跨端适配的边界问题。把这个过程复盘出来是想给准备在OpenHarmony设备上跑RN代码的团队提个醒RNOH的方案已经能支撑实际业务项目了但你不能抱着“代码写一遍处处都能跑”的心态去对待它。尤其像图片选择、日期选择这类强依赖系统能力的组件很可能要做原生适配键盘交互、缓存策略这些看起来不起眼的细节在鸿蒙设备上反而最容易翻车。如果你也在做类似的项目我建议从一开始就把原生桥接层和业务逻辑层彻底解耦把所有鸿蒙特有的适配代码都收拢到一个原生模块目录里。这样后续RNOH版本升级或者你决定回归原生RN时业务层的代码几乎不用动成本会低很多。这个模块后续还可以拓展的方向也不少比如支持AI自动读取身份证信息填充资料、头像的AI生成与美化、资料的跨端一键同步。不过这些都是“锦上添花”的事了先把基础体验做扎实比什么都强。
返回列表