遇到的问题
最近在做一个论坛功能,发帖和评论输入都需要保存到数据库。
而测试测了emoji保存是有问题的,查了数据库是没存进去,google查了一下,emoji是4个字节的,需要将
MySql数据库字符集改由utf8改为utf8mb4,于是把找到的帖子给DBA,DBA看了说不能改,可能会影响现有的数据。
那就只好自己动手,丰衣足食了。
(如果下面的emoji没有正常展示请使用最新版的火狐浏览器)
emoji 存储及展示 前端解决思路
既然直接存储emoji有问题,就改为存储emoji的转换结果。
于是沿着这个思路,首先想到的是 charCodeAt/fromCharCode1
2const charCode = '☠️'.charCodeAt(0) // 10084
String.fromCharCode(charCode) // '☠️'
这么简单,完美!
如果我没遇到这个 🤣 的话1
2const charCode = '🤣'.charCodeAt(0) // 55358
String.fromCharCode(charCode) // '\ud83e'
What ???
让我们来看看 String.prototype.charCodeAt()
charCodeAt() 方法返回0到65535之间的整数,表示给定索引处的UTF-16代码单元 (在 Unicode 编码单元表示一个单一的 UTF-16 编码单元的情况下,UTF-16 编码单元匹配 Unicode 编码单元。但在——例如 Unicode 编码单元 > 0x10000 的这种——不能被一个 UTF-16 编码单元单独表示的情况下,只能匹配 Unicode 代理对的第一个编码单元) 。如果你想要整个代码点的值,使用 codePointAt()。
上面 fromCharCode 得到 \ud83e 的原因是 🤣 由两个编码单元组成,上面引用 MDN 的这段话也推荐了我们使用另一个API codePointAt。
而与之对应的 fromCharCode 则相应变成要 fromCodePoint。1
2const charCode = '🤣'.codePointAt(0) // 129315
String.fromCodePoint(charCode) // '🤣'
完美解决由🤣这类emoji产生的问题。
实际问题解决方案
现在用户输入了下面这段字符串:1
const str = 'hello world 🤣💯!🙌'
我们可以将它转化为:1
2
3
4const emojiRegex = require('emoji-regex')
const regex = emojiRegex()
const fmt_str = str.replace(regex, (p) => `emoji(${p.codePointAt(0)})`)
console.log(fmt_str) // hello world emoji(129315)emoji(128175)!emoji(128588)
利用正则 emoji-regex 匹配出字符串中所有的 emoji ,然后转换。
实际存到数据库里的是 fmt_str
从数据库取出来前端展示时:1
2
3
4
5
6const emojiDecodeRegex = /emoji\([\d+]\)/g
const ori_str = fmt_str.replace(emojiDecodeRegex, p => {
const filterP = p.replace(/[^\d]/g, '')
return String.fromCodePoint(filterP)
})
console.log(ori_str) // hello world 🤣💯!🙌
总结
除了转换成 emoji(129315) 这样的形式,还可以转换成其它的形式;只要是用户输入可能性非常小的就好。
就算是微信,输入: [鼓掌],并发送也一样会变成对应的 emoji。
最后将其封装为两个方法,方便后续多个地方调用1
2
3
4
5
6
7
8
9
10
11
12
13
14const emojiRegex = require('emoji-regex')
const encodeEmoji = str => {
const regex = emojiRegex()
return str.replace(regex, p => `emoji(${p.codePointAt(0)})`)
}
const deCodeEmoji = str => {
const emojiDecodeRegex = /emoji\([\d+]\)/g
return str.replace(emojiDecodeRegex, p => {
const filterP = p.replace(/[^\d]/g, '')
return String.fromCodePoint(filterP)
})
}
我说:“我们好像在池塘的水底。从一个月亮走向另一个月亮。” ——王小波