Forgemoji

每个内容创作者都应该知道的 emoji 编码知识

Emoji 是文本,不是图片。它们有码点、代理对、ZWJ 序列和异体字选择符。大多数内容创作者把它们当作不透明字形。编码很重要,bug 也很真实。

Lois Chen

Lois Chen·Emoji 文化研究员 + 平台指南撰稿人·2026年6月20日

Hand-drawn infographic: emoji encoding and cross-platform rendering

大多数内容创作者把 emoji 当作不透明的图片。你在选择器里点开,挑一张对的脸,发出去,接收方设备就按它自己的方式渲染。这种方式在 95% 的情况下是可行的。另外 5% , 乱码、断裂的字形簇、问号取代 emoji、平台特定的渲染 , 之所以发生,是因为 emoji 是文本,而编码才是关键。

emoji 是文本,不是图片

关于 emoji 最重要的事实是:它们是文本,不是图片。每个 emoji 都有一个 Unicode 码点(由 Unicode 联盟分配的一个数字),这个码点会被存进文本流里。当你发出一条带 😂 的消息,消息里实际包含的是码点 U+1F602,接收方设备再用自己的字形把它渲染出来。

同一个码点在不同平台上的渲染不同,因为每个平台都画自己的字形。😂 在 iOS 上是一张扁平的黄色脸,在 Google 上是一个斑驳的色块,在 Samsung 上是 3D 渲染,在 Twitter 图片代理上则是高对比度的单色。码点是同一个,绘制是平台特定的。这套模型和拉丁字母完全一样:字母 "A" 的码点是 U+0041,"A" 长什么样取决于字体。

基础:码点与 UTF-8

每个 Unicode 字符都有一个码点,以 U+ 前缀的十六进制表示。Emoji 码点位于辅助多文种平面 (Supplementary Multilingual Plane, SMP) 之内,也就是 U+FFFF 之上。这里就是编码开始变得有趣的地方。

在 UTF-8(目前 Web 上占主导地位的编码)中,每个码点被编码为 1–4 字节。Emoji 始终是 4 字节。在 UTF-16(JavaScript 字符串和 Windows 使用的编码)中,U+FFFF 以上的码点被编码为代理对 (surrogate pair), 两个 16 位代码单元合起来表示一个码点。这就是为什么 emoji 在 JavaScript 里是 2 个字符长、而不是 1 个:"😂".length === 2。这让很多前端代码在字符计数时踩坑。

常见 emoji 码点

Emoji码点UTF-8 字节UTF-16 代码单元
😂U+1F60242
❤️U+2764 U+FE0F64
👨‍👩‍👧U+1F468 U+200D U+1F469 U+200D U+1F4671710
🇺🇸U+1F1FA U+1F1F884

最后一行很有趣。🇺🇸(美国国旗)是两个区域指示符码点(U+1F1FA 表示 "U",U+1F1F8 表示 "S")组合成国旗字形。国旗本身没有专门的码点,它们是从这一对码点计算出来的。这就是为什么你输入 "US" 时,只要平台支持区域指示符就能得到 🇺🇸,但底层数据是两个码点、而不是一个。

为什么有些 emoji 是多个码点

有些 emoji 是单一码点(😂 就只有 U+1F602),有些是序列。👨‍👩‍👧(家庭:男人、女人、女孩)是 5 个码点,用零宽连接符 (U+200D) 串起来:U+1F468(男人)+ U+200D(ZWJ)+ U+1F469(女人)+ U+200D(ZWJ)+ U+1F467(女孩)。ZWJ 是一条给渲染器的指令:把我两侧的码点绑定成同一个字形。

这是一套强大的机制。用户可以构造任何组合的家庭 , 👨‍👩‍👧‍👦(两个大人、两个孩子)或者 👨‍👨‍👧(两个爸爸、一个女儿), 只要按顺序敲出码点,即使没有任何平台曾经渲染过这个特定组合。结果取决于渲染平台是否识别这条 ZWJ 序列。大多数现代平台都识别,但渲染可能有差异,旧平台可能把 emoji 显示成几个独立的字形。

代理对与 JavaScript

代理对问题是 JavaScript 中大量 emoji bug 的源头。JavaScript 字符串是 UTF-16,U+FFFF 以上的码点会被存成两个 16 位代码单元(高代理 + 低代理)。也就是说 "😂".length === 2、而不是 1,因为字符串里包含两个 UTF-16 代码单元。

最常见的 bug 是字符计数。一个朴素的 message.length 返回的是 UTF-16 代码单元的计数,而不是用户感知字符的计数。一条带 5 个 emoji、10 个 ASCII 字符的消息,长度会报告 20、而不是 15。修法是使用展开运算符 ([...message].length) 或 Intl.Segmenter API,两者都按码点或字形簇计数。

异体字选择符

异体字选择符 (Variation Selector) 是改变前一个码点渲染方式的码点。最常见的是 U+FE0F(VARIATION SELECTOR-16,VS-16),它强制把一个同时具有文字风格的字符,改成 emoji 风格来渲染。

最清晰的例子是心形。❤(U+2764)默认渲染为文字风格的心形 , 一个厚重、暗红、像印刷装饰符号的图案。❤️(U+2764 U+FE0F)是同一个心形码点加上 VS-16,强制改成 emoji 风格 , 一个鲜红、有光泽的心形字形。两者渲染差异很大,差异仅在 1 个码点。

这就是为什么你在 emoji 选择器里输入"heart"得到的结果,与手敲 Unicode 名称碰运气得到的结果不一样。选择器会自动加上异体字选择符,而手敲裸码点则不会。

肤色修饰符

Fitzpatrick 量表的 emoji 修饰符(U+1F3FB 到 U+1F3FF)可以改变支持它的 emoji 的肤色。🏋️(举重选手)是 U+1F3CB(举重选手)+ U+FE0F(VS-16)+ U+1F3FB(浅色肤色)三者的组合。不加修饰符,默认渲染为黄色 emoji;加上修饰符,渲染为特定肤色。

关键是要知道:肤色修饰符只在被明确设计为接受它们的 emoji 上才有效。给一个不支持的 emoji 加上肤色修饰符不会起作用(修饰符会被忽略),而且不同平台对不支持的组合处理不一致。Forgemoji 生成器遵循 Unicode 规范 , 支持的组合会被赋予肤色,不支持的组合使用默认黄色。

常见 bug 与避免方法

  • 数据库乱码。把 emoji 存进期望 ISO-8859-1 或其他前 Unicode 编码的数据库,会破坏数据。整个技术栈统一使用 UTF-8。
  • 长度计算。朴素的字符计数会低估 emoji 密集内容的"用户感知长度"。改用字形簇(grapheme cluster)计数。
  • 搜索索引。如果搜索引擎按空白分词,emoji 会被当作周围文本的一部分被索引。大多数现代搜索引擎(Elasticsearch、OpenSearch、Algolia)在正确的分词器下能正确处理 emoji。
  • 无障碍性。屏幕阅读器会念出 CLDR 短名。对于自定义 emoji 或 AI 生成的 emoji,这个短名可能是错的或缺失的。请用屏幕阅读器测试。
  • 数据库存储限制。VARCHAR(255) 数的是代码单元、不是字符。MySQL 使用 utf8mb4 编码的 VARCHAR(255) 列能存 255 个字符,但字节上限是每字符 4 字节,所以 emoji 密集内容的实际存储预算是 1020 字节。相应地做好规划。

实操要点

  • 整个技术栈统一使用 UTF-8。2026 年没有任何理由还使用前 Unicode 编码。
  • 对长度敏感的业务(数据库列、字符计数器),按字形簇或码点计算,不要按 UTF-16 代码单元。
  • 在对你用户重要的那些平台上测试 emoji 渲染。iOS、Android、Windows 的渲染各不一样。
  • ZWJ 序列请在老设备上测试。2018 年的渲染不一致,2026 年也还没完全一致。
  • 异体字选择符是重要的。想要 emoji 风格就加 VS-16,不想要就别加。

Forgemoji 输出干净的透明 PNG。编码是你要解决的问题,不是我们的 , 但我们在工程笔记里有一份关于 ZWJ 序列的深度技术文档。

了解实现原理 →

参考资料

推荐继续阅读

  • Emoji 如何成为一种语言:从 Unicode 符号到文化速记 , 为什么码点与名称很重要
  • Emoji 无障碍指南:让自定义 emoji 对每个人都可读 , 把编码知识落到实际
  • 我们如何打造一个支持透明 PNG、GIF、WebP 导出的 AI Emoji 生成器 , 为什么导出格式会有差异

来源

Source: Unicode 16.0 核心规范 , 补充字符与 emoji Unicode 联盟(2026 年 6 月核验)

Source: MDN , 字符串 length 与代理对 Mozilla 开发者网络(2026 年 6 月核验)

Lois Chen

Lois Chen·内容编辑

审核日期:2026年6月20日

写作说明:博客文章基于第一手的平台测试(Discord 服务器、Telegram 群组、TikTok),对 r/discordapp 和 Telegram 贴纸社区中重度用户的采访,以及每周的 Unicode 发布说明检查。每篇指南都至少经过一位编辑的技术准确性审阅,并在相关平台规则变化时更新。表情使用数据来自公开的 Google Trends、UDF(Unicode 表情使用频率)报告以及我们自己的 Forgemoji 生成日志。

来源:Forgemoji 内部编辑团队 — 详细作者信息见「关于我们」页面