在HarmonyOS Next上跑React应用,这个需求现在越来越常见。很多团队手里都有一套成熟的前端代码库,不可能全用ArkTS重写一遍,于是“React跑进鸿蒙App”成了大家最关心的话题。这个开源教程系列已经写到第十篇了,前面聊过工程搭建、路由、状态管理,这篇我单独把“组件化开发”拎出来说说,因为它直接决定了你的项目能活多久、能多快迭代。组件化听着简单,真正落地的时候,拆分的粒度、通信的方式、原生侧和前端侧的边界——每一项都有讲究。这篇不会只讲概念,我会结合自己在HarmonyOS APP里集成React的实际项目经验,把设计思路、实操步骤和踩过的坑一次说清楚。
组件化开发这个命题,放在鸿蒙场景里比纯Web项目更复杂,因为你面对的是一套双端架构:HarmonyOS原生层负责容器能力,React层负责业务界面。两边的组件怎么划分、怎么通信、怎么管理依赖,直接决定了项目的可维护性和团队的协作效率。
1. 整体设计与思路拆解
1.1 为什么要在HarmonyOS里聊React组件化
先搞清楚一个基本问题:鸿蒙App里为什么要用React?原因很简单,业务需要。很多公司已经有成熟的Web端业务,用户基数大、迭代快,如果在鸿蒙上重写一套ArkTS界面,等于两套代码、两套维护,成本和风险都翻倍。反过来,如果直接把React Web应用塞进WebView,界面、交互、登录态都能复用,团队也能继续用熟悉的技术栈——这就是现实中最快的落地路径。
我做过几个鸿蒙项目,最早是纯ArkTS开发,后来接了React应用进去,整个节奏明显不一样。纯ArkTS适合系统能力强的界面,比如复杂的动画、原生控件、硬件交互,但写业务列表、表单、数据展示,React的开发效率明显更高。组件化之后,前端组件的复用逻辑也能平移到鸿蒙容器里,不用重复写一遍。所以我的选择是:原生层只做壳和系统能力,React层管业务,两侧通过组件化的方式组织协作。
但事情没有这么简单。React组件化是前端老话题,函数组件、Hooks、组合模式这些大家都会。到了鸿蒙场景,多了几层约束:Web组件怎么承载React的路由?前端怎么调起原生的扫码、定位、分享能力?离线包怎么管理?页面加载白屏怎么排查?这些都不是React单侧能解决的,需要把“组件化”的视野拉宽,看到整个App的层面。
1.2 三种技术路线的选型取舍
把React和鸿蒙结合起来,市面上无非三条路线,我实际评估过,给你做一个横向对比。
第一条是ArkWeb加载React Web应用。这条路径最简单,把React构建后的静态资源放进鸿蒙工程的rawfile目录,用Web组件直接加载。优点是开发效率最高,Web端的代码几乎零改动,前端生态全能用;缺点是交互体验受限于Web容器,复杂的原生能力需要桥接。
第二条是React Native风格的端侧渲染。通过自绘引擎或鸿蒙兼容层把React组件映射成原生控件,体验接近原生,但底层复杂度高,第三方库适配困难,工程调试成本不低。早期我在调研时试过,环境的配置就足够折腾一星期。
第三条是用ArkUI声明式语法模仿React组件化。这条路不碰React,只是在鸿蒙侧套用组件化的设计思想,比如@Component装饰器、@State响应式数据。代码最“原生”,鸿蒙的特性支持最好,但等于放弃了前端代码复用,人力成本是最高的。
实际项目里我多数时候选第一条,理由很直接:最优ROI。鸿蒙生态还在高速迭代,团队应该把精力放在业务上,而不是花几个月去调底层渲染。ArkWeb本身基于Chromium内核,对React应用的兼容性很好,先跑起来比什么都重要。等到用户量上来、体验瓶颈出现,再针对具体页面做原生化改造也不迟。这个思路就是“渐进式增强”,用组件化把前端和原生两侧解耦,哪天要替换某一侧的实现,不影响整体架构。
1.3 组件化的本质是工程治理,不是文件拆分
说了这么多,我特别想澄清一个误区:组件化不等于把UI切成小块。很多人一谈组件化就想着把页面拆成几十个TSX文件,最后确实拆了,但改一个需求要翻遍七八个文件,那叫拆碎,不叫组件化。
组件化的本质是工程治理,是把“变更隔离”做到位。我做拆分时关注三个目标:第一,独立开发,组件之间互不阻塞;第二,独立测试,每个组件的边界清晰,能单独验证;第三,独立发布,某个模块的改动能单独上线,不影响其他模块。在鸿蒙场景下,还要加一条:前端组件和原生能力的耦合足够松,替换任何一侧不会炸掉整个App。
想清楚这个,拆分粒度就有了判断标准,后面第2章我会详细展开。
2. 核心设计思路与关键决策点
2.1 组件拆分的粒度:按变更频率分,不按UI分
组件拆分的标准,我跟别人聊的时候喜欢用一个生活类比:你把一套房子分给几家人住,分房间是按生活习惯分,还是按墙的位置分?当然是按生活习惯。组件也一样,边界是“人”和“业务”的边界,不是视觉区域的分界。一个表单组件、一张卡片组件、一个弹窗组件,如果它们永远一起改动,那它们本质是一个组件,拆开只会增加通信成本。
我常用的判断标准是“变更频率+业务归属”。比如商品详情页里的价格区、促销区、评价区,这三个区域经常一起变,因为运营活动和商品信息是同一套业务,我倾向放到同一个业务组件里。而一个全局弹窗,承担的是通用反馈能力,不归属任何业务,就提到基础组件层,谁都能引用。这样拆完后,每个组件的生命周期非常清晰,谁改动不影响谁,测试也容易覆盖。
在鸿蒙场景里,还有一个特殊的拆分维度:前端侧和原生侧。我习惯把原生能力跑封装成“平台能力模块”,比如定位、扫码、分享、支付,它们对应一组稳定接口;React侧的组件只关心调用这些接口,不关心底层是谁实现的。这个隔离做好了,以后原生能力升级、替换实现,前端一个文件都不用改。
2.2 分层架构:从组件Atom到业务模块
我自己在鸿蒙项目里建立了一个四层结构,分享出来供你参考。这个结构参考了前端设计模式里的原子设计思想,但结合了双端场景做了调整。
最底层是基础组件层,对应前端设计模式里的Atom,比如Button、Input、Cell、Tag这类通用UI元素。这层的组件必须足够小、足够纯,不掺任何业务逻辑,只通过Props控制外观和行为。举个例子,标题栏这类高频出现的组件,在项目初期就要定好,否则后面每个页面写一套导航,样式到后期必然漂移。
往上是业务组件层,对应Molecule,由多个基础组件组合而成。比如一个“用户信息卡片”,内部有头像、昵称、等级标签,它接收一个用户对象,剩下的格式化和布局自己搞定。“订单状态流”也是一样,接收订单状态字段,自动渲染步骤和时间线。业务组件可以被页面直接引用,但不会直接被路由注册。
再往上是页面模块层,对应Organism,一个路由页面就是一个页面模块。页面模块负责调用业务组件、请求数据、管理页面级别的状态,通常不关心具体UI细节。在React里就是一个顶层容器组件,配合React Router或者状态管理库使用。
最上面是双端协作层,这是鸿蒙场景特有的。这一层单独管理Web组件、原生桥接、离线包检查和JSBridge通信。前端组件不直接碰原生对象,而是通过这一层暴露的封装接口做交互。我把这套协作逻辑也封装成了组件,命名为H5PageContainer,后面会讲它的实现细节。
分完这四层之后,新需求进来,我能很清楚知道该改哪里:改视觉风格,去基础组件层;改业务场景,去业务组件层;改页面流程,去页面模块层;改原生能力对接,去协作层。每一层各司其职,不会互相污染。
2.3 组件通信方案怎么做才不踩坑
组件拆分完之后,通信就是下一个必须解决的问题。React侧组件通信的老三样:Props向下传数据、CallBack向上抛事件、Context跨层级共享。在鸿蒙双端场景,我额外加了两个维度:前端与前端业务模块通信,前端与原生能力通信。
模块间通信,我踩过最深的一个坑是滥用全局状态。早期为了图省事,把各种业务数据都塞进Redux,结果一个小改动引发全量渲染,排查效率极低。后来定了规矩:能用Props传的一律用Props,能用组件内State管的绝不上全局Store,只有跨页面、跨模块的登录态、用户信息、全局配置才进全局仓库。这个规矩执行下来,组件复用度反而更高了,因为依赖的东西更明确了。
前端与原生通信,技术方案是JSBridge。React侧通过Bridge调用原生能力,比如获取设备标识、扫描二维码;原生侧也可以主动推事件给React,比如网络状态变化、App进入后台。这层通信的接口定义要非常严谨,我建议全部采用Promise风格,并且约定统一的失败返回结构,包括错误码和错误信息。这样React侧拿到结果时能做统一处理,不用每个调用点单独判断。
沟通协议定了,剩下的就是具体的桥接实现,这块在第3章的实操环节我完整展示一套可用的代码。
3. 实际操作:在鸿蒙工程里落地React组件化
3.1 工程目录怎么规划
确定了技术路线和组件层次,接下来就是把东西落到工程里。我惯用的目录结构长这样,React项目和鸿蒙项目互相独立,通过构建产物连接。
前端React项目里,目录按组件分层组织:components目录放基础组件和业务组件,pages目录放页面模块,store目录放全局状态,api目录放接口请求,bridge目录专门放JSBridge通信的封装。这个结构的核心原则是“按类型分目录,按领域分命名”,查找任何组件都符合直觉。
鸿蒙原生工程里,我单独开一个h5目录存放React构建产物,路径通常放在entry/src/main/resources/rawfile/h5下。Web容器页面统一收敛到一个组件里,不散落在各个页面代码中。每次构建React代码后,通过脚本自动把dist目录拷贝到rawfile/h5,省去手动复制粘贴的重复劳动,避免漏拷文件导致线上白屏。
我额外在工程里放了一个version.json记录前端包版本。原生侧在启动时会检查这个版本号,如果有更新就优先加载新包;这个能力看似简单,实际解决了WebView缓存带来的一大半难缠问题,后面第4章会说详情。
3.2 构建React应用:需要注意的几个关键参数
React应用本身的构建步骤没什么稀奇,但放到鸿蒙Web容器里,有几个参数必须提前处理。
第一是路由模式。React Router有BrowserRouter和HashRouter两种模式,BrowserRouter依赖History API,需要服务端配合做fallback,但本地file://环境没有服务端,刷新后必然404。所以必须用HashRouter,路由全部走#/,这是我在第一个项目里踩出来的坑,当时排查了很久白屏问题,最后才发现是路由模式不对。
第二是静态资源路径。因为Web组件加载的是本地文件,所有图片、JS、CSS的绝对路径前缀会失效。解决方式是在Vite配置里设置base: './',让产物里全部走相对路径。这个不改的话,样式和脚本全部加载失败,页面直接空白。
第三是WebView的适配。React应用通常按现代浏览器标准书写,Web组件默认不一定开启全部能力。我实际测试下来,至少要保证JavaScript、DOM存储、文件访问、在线图片访问这几个开关是打开的。域名白名单和证书校验的相关配置,如果你用到外部API也需要提前规划。
构建完成后,手动把产物放到rawfile/h5目录,或者写一个npm脚本自动执行同步。听我一句劝,这个自动同步脚本一定值得写,因为每次构建后手动复制一天两天能忍,三个月后你就会漏败,然后花半天排查为什么线上还是旧代码。
3.3 鸿蒙侧Web容器组件怎么封装
鸿蒙侧的Web容器,我建议别直接在业务页面里写Web组件,而是封装成一个公共组件。它的职责有三块:加载页面、注入桥接对象、处理容器状态。
加载页面核心是一句ArkWeb的声明式组件代码,用$rawfile指向本地资源路径,同时创建WebviewController来管理导航和控制。关键配置项涉及JavaScript开关、DOM存储、文件访问和在线图片访问。每次API版本更新我都会复查一遍这几个开关,因为默认值偶尔有变化,不检查就踩雷。
桥接对象注入是双端通信的关键。鸿蒙侧通过javaScriptProxy方法把一个原生对象暴露给前端,methodList里列出前端可调用的方法名。React侧在H5页面里通过window对象访问这个代理,调用时传入参数,原生侧执行完逻辑后把结果回传给回调函数。这套机制和安卓的addJavascriptInterface非常像,写过安卓的同学应该能秒懂。
容器状态管理包括加载进度、加载失败、页面刷新。我在封装里加了一个加载进度条,监听Web组件的加载事件实时更新进度;加载失败时显示错误占位和重试按钮。这些细节看起来不起眼,但对体验影响很大,用户打开App如果一直白屏,会以为应用坏了。
容器页面同时监听了前后台切换事件,在页面进入后台时暂停不必要的H5动画或定时器,回前台时恢复,这样可以降低一点电量消耗。这个机制实现成本不高,但对长期使用的App来说是一笔划算的优化。
3.4 React侧桥接模块怎么设计
React侧的桥接设计,目标是让业务组件不直接感知“我在鸿蒙容器里”。我封装了一个统一的能力调用模块,所有对原生能力的访问都通过这个模块。
这个模块暴露一组API,比如getDeviceInfo、scanQRCode、shareText、startLocationWatch。每个API内部封装了调用细节:检查代理对象是否存在、处理超时、统一错误码。业务组件只要import这个模块调函数,完全不关心底层是不是真去调了鸿蒙原生。
关键设计点是Promise化和超时机制。原生调用是异步的,如果不做Promise封装,回调地狱很快会把代码弄得没法看。我把每个调用都包装成Promise,同时设置超时时间,比如10秒内没有返回就reject。因为一旦原生侧崩溃或桥接失败,前端不能永远挂在那里,必须快速失败并给出提示。
再说一下前端事件怎么推给原生。有些场景是原生侧需要知道前端的状态,比如用户是否登录、当前页面是什么。我在桥接模块里也封装了一个emit方法,前端可以广播事件名和数据,原生侧通过Web组件的事件监听机制接收。这套事件机制让双端的信息交换变得对称,前任遇到“Web页面和原生页面状态不同步”的问题,就能用这种方式解决。
3.5 离线包与版本管理实践
Web应用的离线包管理,是鸿蒙Web容器方案里最容易忽视但影响最大的环节。H5资源打进App安装包,如果用户不升级App,是不是永远拿不到新版本?答案是否定的,因为有缓存。
ArkWeb自身有HTTP缓存也有本地存储,如果不做版本管理,前端构建一次传到rawfile,用户能看到新包,但WebView内部可能会缓存旧的HTML、JS、CSS,导致页面白屏或功能混乱。我采取的办法是“版本号+强制刷新”。
每次构建React代码时,我在产物里生成一个version.json,格式大概是一个主版本号加上构建时间戳。原生侧每次启动Web容器时读取这个文件,把它和上次存储的版本号做对比。如果版本号变了,就清一次Web缓存,再加载H5页面;如果没变,直接复用缓存,节省加载时间。这套逻辑跑下来,线上几乎没有再遇到“更新了没生效”的投诉。
版本管理做完了,还可以再进一步:把H5包的下载和更新独立成动态下发任务,这样不必等App发版,前端代码也能热更新。这属于进阶的玩法,但组件化的架构已经为这个能力留好了口子,哪天业务需要,直接在这个基础上去加就行。
4. 调试技巧与常见问题排查实录
4.1 白屏问题可能是这几个原因
H5页面在Web容器里白屏,是我被问得最多的问题,也是排查起来最头疼的问题。白屏意味着页面加载了但没渲染出东西,原因可能出在路由、脚本加载、样式、桥接脚本任何一个环节。我在项目里总结出一套排查顺序,基本把问题定位时间控制在五分钟以内。
第一步,看Web组件本身有没有加载到HTML。用DevEco Studio跑工程,打开H5页面后观察Web组件的页面标题和URL。如果标题是空的,大概率是路径写错了或者rawfile资源没放全;如果标题正常,说明HTML加载成功,问题在JS或CSS环节。
第二步,看JS是否执行报错。鸿蒙的DevTools工具配合真机调试把H5日志拉出来,浏览器Console里的错误一目了然。我项目里最常出现的错误是跨域问题、相对路径写错、某个CDN资源加载失败。找到报错后,解决方式是改代码还是加白名单配置,就非常清楚了。
第三步,看桥接对象是否注入成功。如果React代码在初始化阶段就调用了原生代理,而代理对象还没有注入,页面会被一个阻塞错误卡住。在React入口代码里加一行日志打印window上有没有代理对象,如果打印出来的是undefined,优先去检查鸿蒙侧的javaScriptProxy配置,方法列表和对象有没有写对。
第四步,看缓存。如果本地reload之后还是旧页面,强制清缓存再看。这一条说起来简单,但在线上环境每次都凭运气,所以必须依赖版本管理的自动清理机制。
4.2 组件更新了但线上看不到,多半是缓存
前面提到了缓存难题,这里展开讲讲我的一次真实经历。有一次前端改了某个业务组件的文案,构建产物也确认上传了,但用户手机上就是一直显示旧文案,说什么都不变。一开始怀疑是线上包没传对,后来发现是WebView缓存了旧的JS文件,导致新代码根本没执行。
这个问题的根源在于WebView的缓存策略和浏览器不太一样,尤其是以本地文件方式加载资源时,缓存头的控制权不在我们手里。我后来在Web组件的加载配置里加了一个强制缓存失效的动作,版本号变化时调用API清理缓存。同时在入口HTML里添加了禁缓存的meta标签,让页面每次加载都重新校验资源版本。
更保险的做法是在JS和CSS的URL后面加上版本参数,比如bundle.20250601.js。这样WebView看到的资源地址改变了,自然不会复用旧缓存。这个做法在老牌的Web开发里就是常识,但放到鸿蒙工程里很容易被忽略,偏偏它又是最有效的一招。
4.3 JSBridge调用时好时坏,问题出在哪
原生桥接调用的稳定性,直接决定了双端协作的体验。我有一个阶段经常收到测试反馈,说扫码功能偶尔无响应、有时第一次调用失败第二次就成功,非常诡异。后来逐一排查,锁定在JSBridge代理对象的注入时机上:当H5页面在Web组件加载完成之前就尝试调用原生方法,代理对象还不存在,调用就会失败。
解决方式是双管齐下。第一,React侧在桥接模块里做一个“就绪检测”,如果检测到代理未注入,就等待Web组件加载完成后再重试,重试次数最多三次。第二,鸿蒙侧把代理注入逻辑尽量前置,确保在页面初始化阶段就完成暴露,不给前端留出空窗期。
再有一个常见问题是回调丢失。原生执行完之后调用前端回调,如果回调函数不在全局作用域或者被GC回收了,前端会一直卡在等待状态。我在桥接设计里加入超时保护,前端调用超过10秒就直接抛错,避免界面假死。测试环境验证桥接效果时,把超时时间调长一点,一旦卡住就能快速定位,不会等到超时才发现。
4.4 组件化工程治理的五个坑
工程治理的坑比较隐性,但影响深远。第一个坑是组件库没有版本号。组件改了,引用方不知道,结果旧页面还在用旧实现,新页面用了新实现,UI瞬间不统一。我给公共组件加了版本标记,任何改动必须更新版本和变更说明。
第二个坑是基础组件随意膨胀。公共组件今天加一个业务字段,明天加一个特殊样式,早期项目都这样,过不了多久就成了“全能组件”,没人敢改。我在规范里明确:基础组件发现业务逻辑侵入,必须拆出业务组件,基础组件保持纯净。这个规矩需要culture支持,团队里得有个人盯。
第三个坑是双端接口没有文档。JSBridge的API如果靠口口相传,新人接手时必然一头雾水。我把所有桥接方法用TypeScript类型定义写清楚,附带参数和返回值说明,生成一份接口文档放在项目根目录,每次更新同步维护。
第四个坑是样式隔离失效。Web应用内部,如果基础组件不约定CSS作用域,全局样式很容易互相污染。我在工程里启用了CSS Modules,每个组件的类名自动加哈希后缀,从编译层面杜绝了样式串扰。这个改造当时花了点功夫,但改完之后组件复用彻底安心了。
第五个坑是没做组件的性能检视。组件再整洁,如果渲染循环里写了大计算量逻辑,或者每个列表项都独立请求接口,用户感知就是卡卡卡。我在开发规范里要求每个列表组件必须使用虚拟滚动,每个接口请求必须带缓存策略,一旦出现性能问题,优先查组件是否违反了这些规则。
5. 工程规范与实践心法
5.1 一套可执行的React组件开发规范
组件化的理念要落地,离不开一套明确的开发规范。这里把我目前项目里在用的规范精简后分享出来,你有需要可以直接抄。
命名上,基础组件文件夹全部小写,比如components/button、components/switch;业务组件用领域名称开头,比如components/user-card、components/order-status。每个组件一个目录,目录下包括主文件、样式文件、类型定义文件和组件README说明。README记录这个组件是什么、什么时候用、什么时候不要用、依赖了哪些原生能力。文档看起来增加了工作量,但半年后回来看,能省下大量翻代码的时间。
组件接口设计上,所有对外暴露的Props必须用TypeScript严格定义,禁止any扩散。回调事件统一以on开头,比如onChange、onClick、onAction。数据请求不在组件内部直接做,而是通过传入数据或Context获取,保持组件的纯展示性质。这个规矩保证了组件可以独立测试——不依赖环境,不依赖数据,传入相同的Props就渲染出相同的结果。
样式管理方面,我倾向Tailwind CSS配合CSS Modules双轨制:布局类、间隙类用Tailwind工具类,组件专属设计变量用CSS Modules定义。这套组合既保证了开发速度,又防止了样式覆盖失控。鸿蒙Web容器对这两种方案兼容性都很好,没有遇到特别诡异的问题。
5.2 组件测试与验收标准,怎么做才不流于形式
很多团队说做了组件测试,但最后就是跑一下“能编译、不报错”。我觉得组件测试要做出价值,至少覆盖三个层面:交互行为、原生桥接、渲染纯度。
交互行为测试我用React Testing Library,模拟用户点击、输入、切换Tab,然后断言组件状态变化是否符合预期。这个层面能抓出大部分交互逻辑Bug,比如按钮连点、表单校验顺序错乱。
原生桥接测试在我的环境里没办法完全自动化,因为真正的鸿蒙原生环境不好在CI里模拟。我的做法是把桥接模块设计成可以mock的,测试时注入一个假的原生代理,验证前端调用参数是否正确、返回值是否被正确处理。在CI流程里,有一个专门的job做这个mock测试,保证桥接层的逻辑每次都被验证。
渲染纯度测试更简单:给同一个组件传入同一组Props,渲染结果必须一致。这个我用快照测试实现,只要组件输出变了,测试就会失败,倒逼开发确认“这个改动是否有意”。如果是有意的UI更新,那就更新快照;如果是无意引入了副作用,问题就能在合并前被发现。
验收标准方面,我定了一条铁律:任何组件不允许包含对全局命名空间的直接修改,不允许直接操作DOM,不允许访问location对象(必须在页面模块层做)。前端设计模式里的纯组件理念,在鸿蒙Web容器场景同样适用,守住这四条,组件就能安全复用。
5.3 团队协作:组件化的另一半实际上是管人
组件化做到最后,你会发现技术问题反而好解决,真正的难点是让团队里的每个人都遵守同一套约定。技术人员有个通病是“我自己来更快”,但组件化要求的是“这一次慢一点,让后面快很多”。我在项目里推行组件化时,做的最重要的事不是写代码,而是每周开组件设计评审会。
评审会上,开发同学把新增组件或改动组件拿出来讲,说明拆分理由、接口设计和边界划分。其他人负责挑毛病:这个组件是不是太笼统了?这个Props是不是太多了?这个样式为什么不做成继承?评审的目的是提前暴露设计问题,而不是事后返工。坚持两个月,团队对组件化的理解会从“技术名词”变成“工作习惯”。
我还建立了一个公共组件文档站,每个组件都有Demo页面,可以在App里直接查看效果。新同学入职第一周,先让他浏览一遍组件库,再模仿其中一个组件写一个新增业务组件,这样比看架构文档领悟得快得多。这套做法看起来花时间,但长期看,每个组件都是一份活文档,不依赖哪个人的记忆。
5.4 不方便的“伪组件化”做法,我劝你直接放弃
市面上有不少团队说自己在做组件化,实际上做的只是把文件分成几十个小块,但没有明确的层次和依赖关系。这种“伪组件化”比不做还可怕,因为它让代码变得难懂,同时破坏了原有的整体性。
常见症状一:看着是组件,实际全在改全局状态。组件只在内部展示,所有数据都在外部Store里,交互逻辑也写在外部,组件自身没有任何内聚度。这种组件没法独立测试,也没法复用,改了等于白改。
常见症状二:组件通信靠props层层透传,中间改了七八层才到达目标。一旦属性名改动,所有中间层都要跟着改,牵一发动全身。我见过一个大组件,props传到第7层已经面目全非,根本没法判断哪些是真正需要的。
常见症状三:组件依赖“隐式全局”。组件内部访问一个挂在window上的公共对象。单看这个组件你根本不知道它跑起来还需要什么环境,换一个容器就全崩。
这些做法的共同点是:把组件化理解成了“文件切分”,而不是“边界管理”。真正能够落地的组件化,必然是以边界的稳定为前提的。边界清晰、依赖单向、接口稳定,组件才谈得上独立演进。
6. 结语与个人经验补充
把React组件化搬进HarmonyOS APP,这一路下来,我最大的感受是:组件化的价值不是让代码“看起来更工整”,而是让业务能持续以更高速度迭代。React层的组件边界清晰,团队能并行开发不再互相踩脚;双端桥接层定义稳定,前端和原生互不拖累;版本管理兜底,更新发版不再提心吊胆。这套架构跑顺了,真正用起来的团队才知道有多省心。
最后再分享一个小技巧:在鸿蒙侧封装Web容器组件的时候,我特意把“加载失败重试”这个能力做得特别完整——包括失败页展示、自动重试上限、手动重试按钮。起初觉得这是小事,后来一次线上资源服务器抖动,多少用户靠这个重试按钮把页面拯救了回来。稳定性和体验,往往就是这些不起眼的细节堆出来的。
如果你正准备在鸿蒙项目里接React,我的建议是不要一上来就追求复杂的设计模式,先把第2章的拆分粒度想清楚,把第3章的桥接层搭好,再按第4章的排查清单做好预案。架构是慢慢长出来的,不是一次规划出来的。希望这篇教程的实操细节,能让你在组件化这条路上少掉几个坑,早点看到业务落地的效果。