一个 bug 的心路历程…

😀:新项目耶 ✌

🤔:怎么做是最佳实践呢

😁:终于好了

😎:测试通过,顺利上线

😱🥵😡:什么,线上 bug!

一般而言,不推荐前端处理 emoji,最好的方式还是把数据库修改成 utf8mb4格式,但是,如果数据库不在你这里呢?如果是一个延续性项目呢…那前端可能没办法,就要自己去处理 emoji 了。

emoji 是什么

emoji 是字符,不是图片。emoji 使用 Unicode 编码方式,可以方便的转换成其他格式。

维基百科关于 emoji 的介绍

charbase.com ,网站提供了,各种格式 emoji 编码的查看方式:

笑哭

上图可以方便的查看笑哭的 Unicode 编码:U+1F602,Javascript Escape:”\ud83d\ude02”等。

常见 emoji 处理方式

  1. 后台更改数据库格式为utf8mb4格式
  2. 使用文字替换,比如微信,QQ 等是使用[笑哭]格式表示 😂,
  3. 文字替换,但是不使用系统的 emoji,而是根据文字显示图片,实现多端显示统一
  4. 使用系统自带的 emoji 表情

emoji 常用转换函数

转换函数,涉及到 unicode 的编码算法,属于专有领域知识,比较复杂,需要的时候复制就行了…不必深究。

方法一:转成Unicode

// https://github.com/channg/umoji/blob/master/src/emojiToUnicode.js
// 转换为js编码方式  😀=>"\ud83d\ude00"
function emojiToUnicode(emoji) {
  let backStr = "";
  if (emoji && emoji.length > 0) {
    for (const char of emoji) {
      const index = char.codePointAt(0);
      if (index > 65535) {
        const h =
          "\\u" + (Math.floor((index - 0x10000) / 0x400) + 0xd800).toString(16);
        const c = "\\u" + (((index - 0x10000) % 0x400) + 0xdc00).toString(16);
        backStr = backStr + h + c;
      } else {
        backStr = backStr + char;
      }
    }
    console.log(backStr);
  }
  return backStr;
}

方法二:转成HTML实体编码

不推荐!!!

如果显示HTML编码,那么在vue框架中必须使用v-html,这是非常危险的,很容易导致xss攻击!

// https://jordonwang.github.io/2018/06/06/emoji-string/
// 转换为HTML实体字符
function emojiToHTMLEscape(str) {
  var patt = /[\ud800-\udbff][\udc00-\udfff]/g; // 检测utf16字符正则
  str = str.replace(patt, function(char) {
    var H, L, code;
    if (char.length === 2) {
      H = char.charCodeAt(0); // 取出高位
      L = char.charCodeAt(1); // 取出低位
      code = (H - 0xd800) * 0x400 + 0x10000 + L - 0xdc00; // 转换算法
      return "&#" + code + ";";
    } else {
      return char;
    }
  });
  return str;
}

如果想深入研究,可以参考:

移动前端手机输入法自带 emoji 表情字符处理

Unicode 与 JavaScript 详解

bug 场景及解决办法

// 数据来自服务端,我传递时候做JSON编码,\被转义为\\
let serverValue = "\\ud83d\\ude00\\ud83d\\ude00";
let value = JSON.parse(serverValue);
<div>&#123;value&#125;</div>;

看起来是一切正常,没有问题。我传递给后端的数据,做JSON.Stringfy(value)编码,拿到时候,再对字符串做 JSON.parse(value)解码。

但是,线上报错。仔细研究发现,有些数据,并不是我传递给后端的,而这些数据里面含有 JSON 的非法字符,导致 JSON 报错。

解决方式也简单:

  1. 不使用 JSON.parse 解码,直接替换\\为\,就可以直接显示在页面上
  2. 如果使用 JSON.parse 解码,加个 try catch

似乎问题解决了,但是JS似乎并不能很好的处理\\为\,stackoverflow这个方法正则方法可以:

function parseUnicode(str)&#123;
    var r = /\\u([\d\w]&#123;4&#125;)/gi;
    str = str.replace(r, function (match, grp) &#123;
        return String.fromCharCode(parseInt(grp, 16)); &#125; );
    return str;
&#125;

在线测试:

JSON 对字符串的处理

前端使用 json,基本都是对 object 进行编码,解码,很少对字符串进行编码解码。如果 JSON.parse()遇到’',往往会报错,基本上都需要转义为‘’\\‘

自己对JSON规范并不熟悉,关于JSON以后单独再写一篇。

JSON 转义测试

轨道图

总结

问题最终定位在 JSON 的转义上面,虽然天天在用 JSON,但是并不理解,查了很多资料,还是一星半解,这篇博客是很难写好了…