在 Weex 中兼容搜狗输入法,核心就是尊重“组合输入”流程:监听 compositionstart/compositionend,在 composition 阶段不把中间文本当作最终值写回界面,确保 v-model 与 selection(光标位置)分离处理,避免频繁更新导致光标跳动;针对 Android/iOS 的 imeOptions/keyboardType 做差异化设置,并处理好软键盘遮挡与候选窗覆盖问题。下面用通俗的原理讲清,再给出可直接上手的实践和调试清单。
先把概念讲清楚(费曼式)

说清楚三件事就够用了:输入事件长什么样、第三方输入法有什么特别、Weex 的输入控件如何和原生键盘“握手”。
- 组合输入(composition):像搜狗输入法这样的中文输入,是先在候选栏里组字(拼音或其他),这期间会产生 compositionstart、compositionupdate、compositionend 等事件。中间状态不是最终文本,不能当 final value 处理。
- 普通 input 事件:在 composition 之外,input 事件表示用户已经确定了字符。这时可以安全地把值同步回业务层并触发校验、搜索等逻辑。
- Weex 的绑定与光标:Weex(基于 Vue 风格)往往用 value/v-model 双向绑定。如果在 composition 未结束时,把绑定值频繁写回,会触发原生控件重新渲染,从而导致光标跳到末尾或丢失选择区。
常见问题与对应思路
1. 光标跳动或候选词被中断
现象:输入拼音后选词,光标突然跳到末尾,或者选词无法插入。
原因:在 composition 阶段前端把 value 写回(比如 v-model 触发了 set),导致原生输入框内部状态被重置。
解决思路:
- 监听 compositionstart,设置一个 flag(isComposing = true);监听 compositionend,把 isComposing 设为 false 并在 end 时统一处理最终值。
- 在 input 事件里,如果 isComposing 为 true,尽量只记录临时内容,不做强制的 value 回写或触发高消耗逻辑。
- 如果必须在输入中做格式化(比如自动大写、加空格),务必在 compositionend 后统一执行,并尝试恢复 selectionStart/selectionEnd。
2. 安卓上搜狗候选窗遮挡输入区域
现象:候选窗覆盖输入框或遮住页面关键控件。
原因:候选窗是 IME 的浮层,和页面布局独立,Weex 页面没有自动调整视图。
解决思路:
- 在 focus 时获取键盘高度(如果能通过原生回调拿到),并把页面上移或调整滚动容器的 padding-bottom。
- 利用 android:windowSoftInputMode(在容器 Activity 端)设置适当模式,如 adjustResize 或 adjustPan,根据实际效果选取。
- 如果是 Weex 容器里,建议在 native 端暴露一个 resize 回调,前端收到后做平滑滚动到输入框位置。
3. emoji、手写、语音输入导致的特殊字符问题
现象:粘贴或语音输入的结果包含不可见字符、联合字符,或长度计算错乱。
原因:某些输入方式会产生组合字符(grapheme)或零宽字符,简单的 length 统计会误判。
解决思路:
- 做字符限制时,使用基于 Unicode Grapheme Cluster 的分割库(如 grapheme-splitter)或用正则按 Unicode 模式清洗。
- 对于 emoji 建议允许而不是简单剔除;如果业务必须限制,则在 compositionend 或 blur 时做最终校验并给出用户友好提示。
简洁可用的实践代码(Weex + Vue 风格)
下面是一个常见的输入组件模板,核心是处理 composition 与 selection:
<input
:value="value"
@input="onInput"
@compositionstart="onCompositionStart"
@compositionend="onCompositionEnd"
@focus="onFocus"
@blur="onBlur"
/>
对应的逻辑:
data() {
return {
value: '',
isComposing: false,
pendingValue: ''
}
},
methods: {
onCompositionStart(e) {
this.isComposing = true
},
onCompositionEnd(e) {
this.isComposing = false
// e.target.value 是最终选字后的值
this.value = e.target.value
// 如果需要恢复光标,可调用原生接口或记录 selection 后恢复
},
onInput(e) {
const val = e.target.value
if (this.isComposing) {
// 临时显示,不触发昂贵操作
this.pendingValue = val
return
}
// 非组合阶段,正常同步
this.value = val
// 触发搜索或校验
}
}
注意事项:上面示例里尽量避免在 composition 阶段调用 setState 导致重新渲染。如果业务需要格式化输入,延后到 compositionend。
iOS 与 Android 的差异要点
| 平台 | 常见表现 | 调优建议 |
| iOS | 智能标点、候选条行为更“规范”、UITextInputTraits 可控 | 设置 keyboardType、disableSmartPunctuation、在 native 侧处理 inputAccessoryView |
| Android | 第三方键盘差异大、候选窗更容易遮挡、IME options 多样 | 在 Activity 设置 windowSoftInputMode,native 侧可调整 imeOptions 或 onCreateInputConnection |
如何在原生层协同改进体验(可选项)
- 暴露一个键盘高度回调给前端,前端收到后平滑调整页面滚动。
- 在 Android 原生 EditText 上,重写 onCreateInputConnection 来控制输入类型和 imeOptions,尽量与搜狗等主流输入法兼容。
- 在 iOS 端,利用 UITextInputTraits(如 keyboardType、autocapitalizationType、smartQuotesType)降低自动替换带来的问题。
调试与测试清单(必做)
- 在真机上用搜狗输入法测试:拼音选词、英文输入、符号、表情、语音与手写。
- 测试光标定位:快速连续输入、删除、选中再输入,检查光标是否稳定。
- 测试候选窗遮挡:在页面底部有固定按钮或重要表单时,确保输入时页面不会被遮挡关键交互。
- 跨键盘测试:搜狗、百度、系统自带、Gboard 等,确保兼容性。
- 边界情况:超长粘贴、多次撤销(undo)、组合字符、emoji 限制等。
常见误区(顺便说两句)
- 误区一:一律把所有 input 事件都当 final。这会造成组合输入中断和光标跳动。
- 误区二:只在浏览器仿真器测试。第三方输入法行为在真机上才会暴露问题。
- 误区三:把所有平台都用同一套 imeOptions。不同平台需要不同微调。
若还想更稳:进一步优化的几个小技巧
- 在 compositionend 后短延时(例如 0-50ms)再进行格式化,能兼顾流畅性和稳定性。
- 为重要输入(如银行卡号、手机号)提供纯数字键盘或分段输入,减少中文输入法参与。
- 在输入频繁触发搜索或请求时,使用节流或防抖,且在 isComposing 时不触发异步请求。
- 记录并在必要时恢复 selectionStart/selectionEnd,必要时配合 native 接口设置光标。
调试时可打印的若干关键值
- composition 状态(isComposing)
- 每次 input 的 e.target.value
- selectionStart / selectionEnd
- 键盘高度(若 native 可提供)和 windowSoftInputMode
参考的概念名词(方便查资料)
- compositionstart / compositionupdate / compositionend
- input event / keydown / keyup
- IME(Input Method Editor)
- windowSoftInputMode / imeOptions / UITextInputTraits
就这些要点了——如果你在实现过程中遇到具体的光标位置恢复、原生回调或候选窗遮挡的例子,把最小可复现场景和日志贴出来,我可以基于那份数据给出更精确的改法。不过先按上面的流程改改,往往就能把搜狗输入法在 Weex 下的体验提升很多。反正写到这儿我又想起之前修复过一个因频繁 setState 导致连选词都点不开的问题,细节挺容易忽略的……