uni-app如何通过过滤器过滤表情符号

作者:袖梨 2026-08-07

uni-app中过滤emoji最稳妥方式是:Vue2用Vue.filter注册全局过滤器,Vue3建议封装工具函数;正则需覆盖Unicode补充平面并加u标志;@input中实时过滤更可控,注意iOS输入法和Array.from处理组合emoji。

uni-app 里用 Vue.filter 定义全局过滤器过滤表情

直接在 main.jsmain.ts 中注册过滤器最稳妥,避免每个页面重复写逻辑。注意 Vue2 和 Vue3 的 API 差异:

  1. Vue2:Vue.filter('noEmoji', (val) => {...}),注册后所有模板都能用 {{ text | noEmoji }}
  2. Vue3:app.config.globalProperties.$filters.noEmoji = (val) => {...},或通过 app.config.globalProperties 挂载,但模板中无法直接用管道符(|),需改用方法调用

推荐 Vue2 项目用 Vue.filter;Vue3 项目建议封装成工具函数 + computedwatch 处理,别强求管道语法。

正则匹配 emoji 的关键点:Unicode 范围必须覆盖完整

常见错误是只匹配 BMP 平面(u4E00-u9FA5 这类),漏掉 emoji 主要所在的 Unicode 补充平面(如 ?️???)。实际需同时处理:

  1. 基础 emoji:/[u{1F600}-u{1F64F}]/u(表情符号)
  2. 交通与地图符号:/[u{1F680}-u{1F6FF}]/u
  3. 杂项符号与象形文字:/[u{1F700}-u{1F77F}]/u
  4. 补充符号:/[u{1F900}-u{1F9FF}]/u
  5. 零宽连接符、变体选择符等(如 ??):需额外处理 u200DuFE0F

简单粗暴但实用的写法:val.replace(/[u{1F600}-u{1F64F}u{1F300}-u{1F5FF}u{1F680}-u{1F6FF}u{1F1E0}-u{1F1FF}u{200D}u{FE0F}]/gu, '') —— 注意必须加 u 标志,否则 u{...} 无效。

@input 事件中实时过滤比过滤器更可控

用户输入时,v-model 绑定的值还没进 data,靠过滤器只能处理显示层,无法阻止非法字符进入数据源。真正要「禁用」,得在输入事件里截断:

  1. 绑定 @input="onInput",而不是 @change(后者只在失焦/回车触发)
  2. onInput(val) { this.search = val.replace(/[u{1F600}-u{1F64F}u{1F300}-u{1F5FF}u{1F680}-u{1F6FF}u{1F1E0}-u{1F1FF}u{200D}u{FE0F}]/gu, ''); }
  3. 如果用了防抖(比如联想搜索),记得先过滤再节流,否则原始字符串可能已含 emoji 并发往服务端

特别注意 iOS 输入法:中文拼音状态下,@input 会先触发未上屏的拼音,再触发上屏后的汉字。若此时正则误删了拼音中间态,会导致输入卡顿。可加判断 if (event?.detail?.value) 确保取到最终值。

emoji 删除逻辑不能只靠 .length

JavaScript 的 string.length 对 emoji 返回的是码元数(如 ?? 是 5),不是字符数。直接 str.substr(0, str.length - 1) 会切出乱码。

  1. 正确做法是用 Array.from(str) 转成字符数组,再操作:Array.from(str).slice(0, -1).join('')
  2. 或者用正则识别 emoji 结构后删除,例如:str.replace(/(?:[u2700-u27BF]|[uE000-uF8FF]|uD83C[uDC00-uDFFF]|uD83D[uDC00-uDFFF]|[u2011-u26FF]|uD83E[uDD10-uDDFF])/g, '').slice(0, -1) 不可靠,仍建议用 Array.from
  3. 聊天场景下,用户点击一次「退格」应删一个视觉字符,而非一个 UTF-16 码元 —— 这点最容易被忽略

复杂点在于组合型 emoji(??、?❤️?),它们由多个 Unicode 字符拼接而成,Array.from 能正确拆分,但正则很难全覆盖。真要高精度处理,得引入 grapheme-splitter 这类库,uni-app 小程序端需权衡包体积。

相关文章

精彩推荐