1. 项目概述:一个“简单”的邮箱输入框背后涉及哪些事
一开始接到这个需求,领导和我说得很轻松:“在 OpenHarmony 上用 React Native 做一个邮箱地址输入框,很简单的。”
我当时的表情是这样的:嗯,确实,一个输入框而已。但真把一个“邮箱地址输入”当成完整功能来做,里面的门道远比你想象的多。输入框样式、键盘类型、正则校验、错误提示、拼音输入法的兼容、真机上的渲染表现、以及 React Native 代码如何在 OpenHarmony 设备上跑起来——每一样都能单独写一篇踩坑记录。
这篇文章是我整个实战过程的完整复盘,从为什么选择 React Native 开发 OpenHarmony 应用,到工程怎么搭、环境怎么配、代码怎么写,再到真机调试时遇到的白屏、字体适配、键盘遮挡等问题的排查过程,全部讲清楚。适合正在评估 OpenHarmony 应用技术选型的团队、打算入坑 RN 跨端开发 OpenHarmony 应用的开发者,以及单纯想看看“One Code, Multi Platform”在 OpenHarmony 生态里到底有多香的同行。
标题里提到的 OpenHarmony、React Native、邮箱地址输入这三个关键词,全文会反复围绕它们展开。但请放心,我不打算给你念文档,我只说我在键盘上真实敲过、在真机上真实跑过的内容。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 整体设计与技术选型:为什么选择 React Native 开发 OpenHarmony 应用
2.1 技术选型的三个核心考量
OpenHarmony 应用开发有三条主路:一是 ArkTS 加 ArkUI 的原生开发,这是华为主推的路线,性能和系统能力调用最彻底;二是音视频、游戏等重场景用 C++ 加 Native 开发;第三条就是跨端方案,把现有业务代码复用到 OpenHarmony 上,React Native 正好属于这一类。
我这次项目选 React Native,不是因为它比 ArkTS 更“高级”,而是因为团队现状决定的。我们手里的业务代码本来就是 React Native 写的,服务端接口、状态管理、组件封装全部现成,如果为了 OpenHarmony 单独用 ArkTS 重写一遍,等于养两套代码、两个团队,后续每次需求变更都要同步改两边,维护成本直接翻倍。而 OpenHarmony 的 RN 适配方案,也就是 react-native-openharmony 这个项目,正好能把我们现有的 JS 代码跑在 OpenHarmony 设备上,UI 层差异通过条件编译和少量桥接代码处理,业务逻辑百分之百复用。
另外一个现实考量是生态迁移能力。国内现在很多设备厂商都在基于 OpenHarmony 做自己的发行版,未来应用要覆盖的设备品类,手机、平板、电视、带屏设备,只会越来越多。React Native 本身在设计上就强调跨平台复用,做一次适配后,后续新设备平台的接入成本会被摊薄。
2.2 react-native-openharmony 的适配原理一句话讲透
对于没接触过这块的读者,我用一个生活化的类比来解释。React Native 的核心思路就像开一家连锁餐厅,每家门店的厨房设备不一样,但菜单和菜谱完全统一。JS 代码就是菜单,告诉门店“今天要卖什么菜、怎么做”;而原生平台上的渲染引擎和桥接层就是后厨,负责把菜单翻译成本地设备能执行的指令。
在 OpenHarmony 上跑 RN,靠的不是 Facebook 官方支持的 iOS 和 Android 渲染器,而是 OpenHarmony 社区开发的一套基于 ArkUI 组件的渲染适配层。这套适配层实现了 RN 的 Fabric 渲染架构规范,把 JS 侧的 <View>、<Text>、<TextInput> 这些基础组件,映射到 ArkUI 对应的组件上,同时通过自定义桥接模块把 OpenHarmony 的系统能力,比如 Toast、网络请求、键盘事件,暴露给 JS 层调用。
这里有个很重要的点:RN 的代码最终不是原封不动跑在设备上的。它需要经过 Bunlde 打包生成一个 JS 文件,原生端启动时加载这个 Bundle,再由 JS 引擎(OpenHarmony 上用的是 ArkTS 运行时内置的 JSVM)解释执行。所以,工程搭建时最核心的一件事,就是确保 Bundle 能被正确生成并让原生端读得到。
3. 环境准备与工程搭建:完整步骤和避坑记录
3.1 开发环境清单:版本对齐是第一优先级
先给出一份我实际使用的环境清单。别看这张表简单,版本对齐这一步就能劝退不少人。react-native-openharmony 的版本号紧密跟随 React Native 官方版本,官方 RN 升到 0.72,适配层也会跟着升到 0.72.x。如果你随便装一个最新版的 RN,再装一个 OpenHarmony 适配层,版本对不上,那屏幕基本就是白屏起步。
| 组件 | 建议版本 | 说明 |
|---|---|---|
| DevEco Studio | 4.0 Release 及以上 | 华为官方 IDE,用于 OpenHarmony 工程构建与调试 |
| OpenHarmony SDK | API 10 及以上 | 对应系统版本 4.0 Release,rk3568 开发板通用 |
| Node.js | 18 LTS | 用于 RN CLI 和 npm 包管理 |
| React Native | 0.72.x | 适配层当前最稳的基线版本 |
| react-native-openharmony | 0.72.x 对应版本 | npm 上包名为 @react-native-openharmony/... 系列 |
| 鸿蒙开发板 | rk3568 或 Dayu200 | 推荐 Dayu200,社区资料最多 |
3.2 创建 React Native 工程并接入 OpenHarmony 适配层
环境装好之后,第一步是初始化一个 RN 工程。这一步建议用 npx 命令直接从 React Native 官方模板创建,不要手动去拼 package.json,否则依赖版本大概率会乱掉。
bash复制npx react-native@0.72 init RnHarmonyMailInput --version 0.72.5
cd RnHarmonyMailInput
工程创建完成后,目录里默认只有 android 和 ios 两个原生工程目录。要跑 OpenHarmony,需要把 OpenHarmony 的工程结构补进来。react-native-openharmony 官方提供了一套模板,上面这个仓库地址里有对应的空工程模板和示例代码,直接把它拷贝到项目根目录下的 harmony 文件夹即可。
接下来安装适配层依赖:
bash复制npm install @react-native-openharmony/react-native @react-native-openharmony/text-input @react-native-openharmony/toast
这里特别提醒一下,OpenHarmony 的 RN 适配把原本混在 react-native 主包里的组件拆分成了多个子包,比如文本输入框单独一个包、弹窗提示单独一个包。原因是 RN 主包体积很大,如果应用只需要部分组件,全量打包会浪费安装包空间和运行内存。这种按需引入的思路,和现在前端工程里的 Tree Shaking 逻辑是相通的。
依赖装完,打开 harmony 目录下的 oh-package.json5,把适配层的原生依赖加进去。然后打开 entry/src/main/ets/entryability/EntryAbility.ets,确认入口 Page 加载的是 RNOHCoreContext 创建的组件树中的首页,也就是加载 RN 渲染的容器页面。这部分的代码在模板里已经写好了,你只需要确认里面有这样的加载逻辑:
typescript复制// EntryAbility.ets 关键片段
windowStage.loadContent('pages/Index', (err) => {
if (err.code) {
console.error('load content failed: ', err)
return
}
})
其中 pages/Index 内部承载了一个 RNOH 的容器组件,它负责把 JS Bundle 渲染到 ArkUI 的页面节点上。
3.3 打包 Bundle 的两种方式:Debug 模式和 Release 模式
RN 代码在 OpenHarmony 端能跑起来,核心是拿到 Bundle 文件。Debug 模式下,可以通过 Metro 服务实时加载,代码改动保存后立即在真机上刷新,开发体验很接近 Web 热更新,适合快速调试;Release 模式下,则需要先把 JS 代码打包成静态的 index.bundle 文件,放到原生工程的 resources 目录下,应用启动时直接从本地加载。
Debug 模式启动 Metro:
bash复制npx react-native start
然后用 DevEco Studio 运行 harmony 工程到真机。需要注意的是,RN 的 Metro 默认端口是 8081,但 OpenHarmony 真机在局域网内访问电脑的 Metro 服务时,必须确保手机和电脑在同一个网段,同时防火墙放行 8081 端口。如果 Bundle 加载不出来,九成以上是网络不通,不是代码问题。
Release 模式打包:
bash复制npx react-native bundle --platform harmony --dev false --entry-file index.js --bundle-output harmony/entry/src/main/resources/rawfile/index.bundle --assets-dest harmony/entry/src/main/resources/rawfile
这条命令执行完,检查一下 index.bundle 是否出现在 rawfile 目录下,然后重新构建 harmony 工程。Release 模式下应用启动不依赖 Metro,启动速度也会快不少,适合交付测试。
4. 邮箱地址输入功能的完整实现与代码解析
4.1 需求拆解:一个合格的邮箱输入框要覆盖哪些细节
很多开发者拿到“邮箱地址输入”这个需求,第一反应就是放一个 <TextInput>,然后正则校验一下就完了。但实际要交付一个能上生产环境的功能,需求细节比表面看到的要多得多。
我梳理出这样几个子需求:第一,输入框要有清晰的标签和占位提示,用户一看就知道要填什么;第二,键盘类型要适配邮箱输入场景,英文键盘为主,同时要支持 @ 符号和常用域名后缀快捷输入;第三,输入过程中要有实时校验,但不要打断用户的输入节奏;第四,失焦后要有明确的错误提示;第五,输入框右侧最好有一个一键清空的按钮;第六,要支持粘贴邮箱地址,粘贴后自动去掉首尾空格;第七,密码管理器或系统自动填充如果有能力,也要对接上。
这七个点里,第二点和第六点是最容易被忽视的,但也是用户感知最强的。尤其是键盘类型,如果你把 keyboardType 设置成默认,用户在输入 @ 符号时得切换键盘符号页,体验非常糟糕。而带上 autoCapitalize="none" 和 autoCorrect={false} 这两个属性,可以避免 iOS 系键盘把邮箱地址首字母自动大写、自动纠错成奇怪字符串的问题。OpenHarmony 的适配层对这些属性的处理大体对齐了 Android 行为,但也存在小差异,后面我细说。
4.2 核心代码实现:从静态 UI 到完整校验闭环
先看整体代码结构。我拆成了三个文件:MailInput.js 是核心组件,负责 UI 渲染和状态管理;validators.js 放邮箱格式校验函数;App.js 作为页面入口,把组件接入真实业务场景。
validators.js 里的校验函数,我采用的是双层校验逻辑:第一层是快速格式检查,用正则判断邮箱地址的基本形态;第二层是域名合法性检查,判断邮箱的域名部分是否包含常见的顶级域结构。注意,这里我没有做“发送验证邮件”这样的强校验,那属于后端同学的工作,前端只需要保证格式符合 RFC 5322 的通用规范即可。
javascript复制// validators.js
export function isValidEmail(email) {
if (!email || typeof email !== 'string') return false
const trimmed = email.trim()
if (trimmed.length === 0) return false
if (trimmed.length > 320) return false
// 基础格式校验:用户名@域名
const basicPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!basicPattern.test(trimmed)) return false
// 域名部分校验:至少包含一个点,且点后不能紧跟点
const domainPart = trimmed.split('@')[1]
if (!domainPart || domainPart.startsWith('.') || domainPart.endsWith('.')) return false
if (domainPart.includes('..')) return false
return true
}
export function getEmailErrorText(email) {
const trimmed = (email || '').trim()
if (!trimmed) return '邮箱地址不能为空'
if (!trimmed.includes('@')) return '邮箱地址中缺少 @ 符号'
const [localPart, domainPart] = trimmed.split('@')
if (!localPart) return '邮箱地址中缺少用户名部分'
if (!domainPart) return '邮箱地址中缺少域名部分'
if (!domainPart.includes('.')) return '邮箱域名部分不完整'
return ''
}
这个校验函数相比“一行正则搞定”的写法,好处在于错误提示足够具体。用户看到“邮箱地址中缺少 @ 符号”和“邮箱格式不正确”,前者能直接指导他修改,后者只能让人抓瞎。
接下来是 MailInput.js 组件的核心实现。一个完整的邮箱输入组件应该包含文本输入框、标签文字、错误提示文字、清空按钮这几个元素。我在布局上采用了一个外层 View 包裹,内部用 View 嵌套 TextInput 和清空按钮的方式,形成一个输入框的整体容器。
javascript复制// MailInput.js
import React, { useState, useRef } from 'react'
import {
View,
TextInput,
Text,
TouchableOpacity,
StyleSheet,
Keyboard,
} from 'react-native'
import { getEmailErrorText, isValidEmail } from './validators'
const MailInput = () => {
const [email, setEmail] = useState('')
const [touched, setTouched] = useState(false)
const [isFocused, setIsFocused] = useState(false)
const inputRef = useRef(null)
const handleChangeText = (text) => {
// 去掉首尾空格,避免用户复制粘贴时带入多余字符
const cleaned = text.trim()
setEmail(text)
if (touched && getEmailErrorText(cleaned)) {
// 实时反馈,但不强制
}
}
const handleBlur = () => {
setTouched(true)
setIsFocused(false)
}
const handleFocus = () => {
setIsFocused(true)
}
const handleClear = () => {
setEmail('')
inputRef.current?.focus()
}
const errorText = touched ? getEmailErrorText(email) : ''
const showClearButton = email.length > 0 && isFocused
return (
<View style={styles.container}>
<Text style={styles.label}>邮箱地址</Text>
<View style={[styles.inputWrapper, isFocused && styles.inputWrapperFocused]}>
<TextInput
ref={inputRef}
style={styles.input}
value={email}
onChangeText={handleChangeText}
onBlur={handleBlur}
onFocus={handleFocus}
placeholder="请输入邮箱地址"
placeholderTextColor="#999999"
keyboardType="email-address"
autoCapitalize="none"
autoCorrect={false}
returnKeyType="done"
onSubmitEditing={Keyboard.dismiss}
/>
{showClearButton && (
<TouchableOpacity onPress={handleClear} style={styles.clearButton}>
<Text style={styles.clearButtonText}>×</Text>
</TouchableOpacity>
)}
</View>
{errorText ? <Text style={styles.errorText}>{errorText}</Text> : null}
</View>
)
}
4.3 样式细节与真机适配
样式这里最值得说的不是颜色字号,而是“输入框高度”和“点击区域大小”这两个细节。
输入框高度我设置在 44 到 48 dp 之间,这不是我拍脑袋定的,而是移动端触控规范里“最佳点击区域不小于 44x44 dp”的通用建议。如果输入框做成 36 dp,虽然视觉上更“紧凑”,但用户手指粗一点就很容易误触旁边的清空按钮。
清空按钮的点击区域也要给足。我把它做成一个 40x40 dp 的 TouchableOpacity,视觉符号只有 16 dp 的“×”,但热区很大。这样用户在盲操作时,手指按偏一点也能命中。
真机适配方面,有两个和 OpenHarmony 强相关的点。第一是字体渲染。OpenHarmony 默认字体是 HarmonyOS Sans,和 Android 的 Roboto、iOS 的 SF Pro 在字重和字间距上有差异。比如 email-address 键盘输入时,如果设置了自定义 fontFamily,可能出现字体回退导致宽度跳动,在输入过程中布局会“抖”。我的处理方式是不在 TextInput 上强设 fontFamily,让它跟随系统默认字体。
第二是 placeholder 的垂直居中。在部分 rk3568 设备上,如果 TextInput 的行高设置过高,placeholder 会偏上或偏下,看起来像没有垂直居中。这是因为 ArkUI 对 placeholder 的布局采用的是 baseline 对齐,而不是 flex 居中对齐。解决办法是把 placeholder 的 fontSize 和 TextInput 的 fontSize 设成一样,同时不显式设置行高,让系统自行计算。
4.4 对外暴露校验能力与表单联动
实际业务里,邮箱输入框不会是孤立的,它一般和“登录”“注册”“提交”按钮联动。组件设计上,我用 forwardRef 和 useImperativeHandle 把校验方法暴露给父组件,这样父组件可以在点击提交按钮时主动触发校验。
javascript复制// MailInput.js 补充片段
import React, { forwardRef, useImperativeHandle } from 'react'
const MailInput = forwardRef((props, ref) => {
// ...上面所有代码
useImperativeHandle(ref, () => ({
validate: () => {
const cleaned = email.trim()
const error = getEmailErrorText(cleaned)
setTouched(true)
return {
valid: !error,
error,
value: cleaned,
}
},
getValue: () => email.trim(),
}))
return (
// ...UI 保持一致
)
})
父组件里这样调用:
javascript复制const mailRef = useRef(null)
const handleSubmit = () => {
const { valid, error, value } = mailRef.current.validate()
if (!valid) {
Toast.show(error)
return
}
// 走登录/注册逻辑
doSubmit(value)
}
这种设计的好处是校验逻辑完全收敛在输入组件内部,父组件不需要知道“邮箱正则长什么样”,只需要调用 validate() 拿到结果。以后如果需求变更为“手机号或邮箱二选一登录”,只需要在父组件层面组合校验,不用改动输入组件内部。
4.5 输入过程中的体验细节
再补充几个我在实现过程中认为对体验影响极大的细节,这些不是产品经理要求的,但做出来之后产品经理非常满意。
第一个是“失焦校验,输入不打断”。如果用户还没输入完就弹出错误提示,非常烦人。我这里的策略是:用户刚开始输入时只清理明显错误(比如多了空格),不做强校验;只有用户点击输入框以外的区域,也就是触发 blur 事件之后,才显示完整错误信息。这样既不打断输入节奏,又能保证最终提交前用户能看到全部问题。
第二个是“一键清空,焦点不丢”。当输入框有内容且处于聚焦状态时,右侧显示“×”清空按钮,点击后清空文本,同时重新聚焦到输入框。这个功能虽然简单,但对输入场景很实用,尤其是用户想重新输入一长串邮箱时,不用长按删半天。
第三个是“粘贴空格自动清理”。用户从微信或者网页复制邮箱地址时,经常带上前后的空格或换行符。我的 handleChangeText 里用了 trim() 清理首尾空格,这样即使用户不小心粘贴了脏数据,提交到后端的数据也是干净的。但要注意,不要在 onChangeText 里直接修改 state 为 trim 后的值,否则用户在输入框中间编辑时,光标会乱跳。先保留原始 text,在校验和提交时再 trim,是最稳妥的做法。
5. 真机调试与问题排查
5.1 启动白屏问题排查:路由、Bundle 路径和字体一个都不能少
真机调试阶段,我遇到的最大障碍是启动白屏。这个问题其实在 React Native 开发 Android 应用时也存在,但在 OpenHarmony 端发生的概率和原因更多样。我总结出三类最典型的白屏原因,按排查顺序排列如下。
第一类是路由入口的问题。harmony 工程入口加载的是 pages/Index,这个页面必须正确创建 RNOH 容器并加载 Bundle。如果容器没创建成功,页面就是空白的。排查方法是在 DevEco Studio 的 Log 面板里过滤 RNOH 或 RNInstance 关键词,看有没有报错信息。如果看到 loadBundle failed,那基本就是 Bundle 文件没有被正确读取。
第二类是 Bundle 路径问题。Release 模式下,Bundle 必须放在 harmony/entry/src/main/resources/rawfile/index.bundle 这个固定路径。如果你打包命令里的输出路径写错了,或者 rawfile 目录下文件名对不上,原生端找不到文件,自然白屏。注意 rawfile 目录在 DevEco Studio 里有时候不显示,需要打开文件管理器确认文件真的存在。
第三类是字体初始化问题,这个坑比较隐蔽。react-native-openharmony 适配层在启动时会读取系统字体配置,如果 harmony 工程里没有任何自定义字体配置,部分依赖字体测量才能渲染的字符会直接不显示。最典型的就是 iOS 平台默认的 SF 字体在 OpenHarmony 上不存在,RN 适配层会回退到默认字体,但 TextInput 里的光标位置计算可能异常,出现“点了没反应”或“光标不可见”的假白屏。解决办法是在 EntryAbility.ets 里调用字体初始化方法,加载 HarmonyOS Sans 作为默认回退字体。
5.2 键盘遮住输入框:调整 windowSoftInputMode 的正确姿势
输入框在页面靠下位置时,点击后弹出的软键盘经常会遮住输入框,导致用户看不到自己正在输入的内容。这个问题在 Android 上可以通过 AndroidManifest.xml 里的 windowSoftInputMode="adjustResize" 解决。在 OpenHarmony 上,对应的配置在 module.json5 里。
我这里踩了一个坑:把 windowSoftInputMode 配置成 adjustPan 后,键盘虽然能把输入框顶起来,但页面整体布局会变形,输入框上部的内容被挤到屏幕外,而且键盘收起后布局不能完全恢复。换用 adjustResize 后,页面自动调整高度,输入框始终可见,布局保持正常。
不过,adjustResize 也不是万能的。如果你的页面用了 ScrollView 且键盘弹起后滚动位置没有及时调整,输入框仍可能被挡住。我的处理方式是在 TextInput 上监听 onFocus 事件,延迟 300 毫秒后调用 ScrollView 的 scrollTo 方法,把输入框滚动到可视区域中央。这个时间延迟是为了等待键盘动画完成,实测 300 毫秒在大多数设备上表现良好。
5.3 正则校验在不同输入法下的差异
邮箱输入框的校验逻辑在 Android 和 iOS 机器上跑了很多轮都没问题,但到了 OpenHarmony 真机上,用系统自带的输入法输入时,出现了“校验永远失败”的情况。排查半天后发现,问题出在输入法自动添加的“全角字符”上。
部分输入法在中文模式下,会把用户输入的 @ 符号自动替换成全角字符“@”,它的 Unicode 码点和半角“@”完全不同,正则匹配自然失败。这个问题的排查思路是先打印用户实际输入内容的 Unicode 编码。看到 \uFF20 就明白了,这是全角 @。
解决方案有两个层级:第一层是在 handleChangeText 里做一次全角转半角的归一化,把所有全角字符转换成半角字符;第二层是在校验函数里增加对全角字符的容错。我采用的是第一种,因为邮箱地址本身就不应该包含任何全角字符,直接转换反而能统一数据格式。
javascript复制const normalizeEmailInput = (text) => {
return text
.replace(/[\uFF01-\uFF5E]/g, (ch) => String.fromCharCode(ch.charCodeAt(0) - 0xFEE0))
.replace(/\u3000/g, ' ')
}
这个函数把全角字符区间统一转成半角字符,包括全角空格。在 handleChangeText 里先调用它,再更新 state,从源头保证输入数据干净。
5.4 字体与渲染差异:HarmonyOS Sans 下的文本截断问题
最后分享一个视觉层面的小问题。RN 适配层在 OpenHarmony 上渲染 <Text> 组件时,由于 HarmonyOS Sans 的字体度量数据和 Android 默认字体不同,个别字符串会出现底部截断的情况。典型场景是占位提示文字:placeholder="请输入邮箱地址" 如果父容器高度刚好等于字体行高,字母“y”的尾部在部分设备上会被截掉一截。
这个问题的本质是字体度量中的 descent 值差异导致的。Android 的 Roboto 字体 descent 比例较小,而 HarmonyOS Sans 的 descent 稍大,导致基线以下的字符部分超出了容器高度。解决方案是给 TextInput 的容器多留出 2 到 4 dp 的 paddingBottom,或者在 justifyContent 上使用 flex-end 而不是 center 来调整垂直位置。虽然 2 到 4 dp 的差异单看几乎不可见,但在用户逐字输入时,这种细节的“难受感”是真实存在的。
6. 从“能跑”到“好用”:生产级提升的三个额外建议
到这里,邮箱输入框的核心功能已经完整可用了。但如果你想把它放到生产环境,我建议再追加三件事。
第一件是增加“格式化粘贴”能力。很多用户从邮件客户端或网页复制邮箱地址时,会带上前面的“mailto:”前缀。在 handleChangeText 里检测到 mailto: 字符串时自动剥离,这个小功能能减少很大比例的无效输入反馈。
第二件是给输入框增加淡入淡出的错误提示动画。RN 的 LayoutAnimation 在 OpenHarmony 适配层上的表现还不完全稳定,我实测用 Animated.timing 控制错误提示文字的透明度,效果要可靠得多。用户感知上,错误提示“温柔”地出现比突然弹出体验更好。
第三件是接入无障碍能力。给 TextInput 设置 accessibilityLabel="邮箱地址输入框",给清空按钮设置 accessibilityLabel="清空邮箱地址"。OpenHarmony 的无障碍框架会读取这些属性,配合读屏软件使用。这个建议在大多数需求文档里不会写,但对于一个面向公众的应用来说,是必须补上的素质。
我个人在实际操作中的体会是:一个“简单的邮箱输入框”能不能让用户无感,检验标准不是功能列表多完整,而是每一步交互是否都顺应直觉。键盘类型对不对、校验打扰不打扰、粘贴干不干净、字体截不截断,单拎任何一个出来都是小事,但叠在一起,就是产品和竞品的差距。这也是我为什么愿意花一整篇文章来复盘这个“小功能”的原因——在 OpenHarmony 生态还在快速完善的阶段,每个“小功能”的落地,都比在成熟平台上多走几步路。希望这篇记录能帮你把这几步路走得更顺一点。
