这里说的 emoji 是文字形式的表情,如 😆。它不是图片,而是一个文字,只不过显示出来是个表情的样式。
之前本博客的评论框下面有个“添加颜文字”的功能,但是颜文字的实用性不太好,我经常都找不到合适的颜文字。而且作为“表情”来说,颜文字看起来也不太直观,于是我想将其换成 emoji 表情。
代码改起来倒是很简单,但是出现了预料之外的问题:在评论里添加 emoji 并提交评论之后,发出来的评论里并没有显示 emoji 表情。
于是我去数据库里查看评论,发现数据库里确实没有保存上 emoji,只有其他的文字内容。
那么问题是出在入库之前吗?我没有做过去掉 emoji 代码的处理呀,那难道是我所使用的主题里的代码对评论进行了预处理,去掉了 emoji 字符然后提交到数据库?
但是主题为什么要去除 emoji 呢,没理由啊?但我还是查找了一番。我搜索到 WordPress 里对评论内容进行预处理的钩子(filter)是 preprocess_comment
,评论内容是 comment_content
。但是我在主题目录里查找这些字符,并没有找到去除 emoji 代码的操作。
相关文档:preprocess_comment
之后我在谷歌搜索 “wordpress comment emoji not showing” 之类的关键词,还真找到了最可能的原因:数据库字符集。
stackoverflow 上有人遇到了我和一样的情况:
MySQL utf8mb4, Errors when saving Emojis
他在保存 emoji 时出现错误,无法保存,但能够保存太阳的符号 ☀️。
于是我也在评论里试了一下,果然,其他 emoji 无法显示,但是 ☀️ 可以显示:
评论区有人指出了原因:数据库需要使用 utf8mb4 字符集保存文本才能正常保存 emoji。如果使用 utf8 字符集,则无法保存长度为 4 个字节的 emoji 字符。由于太阳 ☀️ 是 3 个字节,所以在 utf8 字符集中可以保存。
我:啊?难道我的数据库字符集不是 utf8mb4 吗?查看了下,还真不是。
执行 show variables like 'character%';
:
赫然显示 character_set_database | utf8
。这是我没想到的。
执行 show create table wp_comments;
也可以看到创建该表时使用的是 utf8 字符集。
归根到底,当初建立这个数据库和表的时候,用的字符集就是 utf8(当时的 WordPress 没考虑过 emoji 文字的事吧)。而 utf8mb4 是 utf8 的超集,可以保存四个字节的 emoji 字符。
解决方法
步骤 1:修改评论字段(列)的字符集:
alter table wp_comments change comment_content comment_content text character set utf8mb4 not null;
将 wp_comments 表的 comment_content 字段重设为 utf8mb4 字符集。
步骤 2(可能不必须):执行命令 SET NAMES utf8mb4
将数据库的链接字符集改为 utf8mb4,这样数据库客户端和服务器都会使用 utf8mb4 字符集来处理数据。
相关文档:SET NAMES Statement
之后在评论里添加 emoji 就可以正常显示了。
所以现在这个问题已经解决啦!大家可以愉快的发 emoji 了。
纯前端的解决办法
如果不想从数据库修改,也有个纯前端的办法,就是将 emoji 表情转换成其十六进制字符代码,这样在网页上可以正常显示为 emoji 表情。
例如这个表情:😆,可以使用以下代码获取其十六进制编码:
const emoji = '😆'
const num = emoji.codePointAt(0)
// 128518
const str = num.toString(16)
// '1f606'
// 在十六进制字符串前面添加 &#x 前缀以及 ; 后缀
const code = `&#x${str};`
// '😆'
可以在用户将 emoji 插入到评论里时进行处理,把 emoji 字符换成其对应的编码。这样就是普通的字符了,可以保存到 utf8 字符集的数据库里,而且也可以在网页上正常显示。
这种编码格式其实类似于转义字符,相信前端对转义字符都很熟悉,例如空格是 & nbsp;
,大于、小于符号是 <
和 >
。而 &#x
前缀则表示后面跟的是个十六进制的数字,浏览器会显示出对应的 Unicode 字符。
这种方法不需要修改数据库,但不够优雅和直观,尽量还是从数据库修改。
在数据库里直接保存 emoji 与保存十六进制编码的结果:
在浏览器里显示时没有区别,因为浏览器会将十六进制编码变成对应的字符。
emoji的惊喜其实还有挺多的。
比如我的站曾经因为对文字做裁切,正好切到emoji导致编码出错全站崩溃的😅。