从零实现一个“类微信”表情输入组件
2025年12月4日 15:06
📋 背景
最近接到一个需求:在系统中添加表情输入功能。由于需要与腾讯某平台保持数据一致,表情包的数量和取值都要完全相同。
翻阅文档后发现并没有提供现成组件,只能自己实现。
先观察了实现方式:打开控制台面板,点击表情后会瞬间请求大量图片。
输入逻辑上,用户选择 😊 后值自动变成 [微笑],用户直接输入 [微笑] 也能映射成对应表情图片。
🚩 最终目标
- ✅ 支持点击表情面板插入表情
- ✅ 支持输入
[微笑]自动转换为表情图片 - ✅ 完成双向绑定,取值时图片转回
[微笑]文本 - ✅ 字符长度计算:中文 1 个字符,英文 0.5 个,表情 1 个
- ✅ 光标定位准确,体验流畅
- ✅ 输入法友好,不在拼音输入阶段转换
🧩 实现步骤
1、获取表情包数据
最初尝试在网上找表情包资源,但数量总是对不上。近百个表情包如果手动逐个校对太过折磨,于是尝试从页面爬取数据。 在浏览器控制台执行以下脚本:
// 获取所有表情元素
const emojiItems = document.querySelectorAll('.emoji-list li');
// 提取关键信息:图片地址、文本代码、文件名
const emojiArray = Array.from(emojiItems).map(li => {
const img = li.querySelector('img');
return img ? {
src: img.src,
alt: img.alt,
dataImage: img.getAttribute('data-image')
} : null;
}). filter(Boolean);
console.log(emojiArray);
执行后直接在控制台复制数组,保存为 JSON 文件。
src 只做下载使用,alt 需要用来做映射,dataImage 用于拼接读取表情图片路径。
[ { "alt": "[微笑]", "src": "https://xxx.qq.com/xxx/emojis/smiley_0.png" "dataImage": "smiley_0" }]
2、批量下载图片
使用 Node. js 脚本批量下载表情图片:
const fs = require('fs');
const path = require('path');
const outputDir = path. resolve(__dirname, 'emojis');
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
async function downloadImage(item) {
const fileName = `${item.dataImage || item.alt || 'emoji'}. png`;
const filePath = path.join(outputDir, fileName);
try {
const res = await fetch(item.src);
if (!res.ok) throw new Error(`Failed to fetch ${item.src}`);
const buffer = await res.arrayBuffer();
fs.writeFileSync(filePath, Buffer.from(buffer));
console.log(`Downloaded: ${fileName}`);
} catch (err) {
console.error(`Error downloading ${item.src}:`, err.message);
}
}
async function downloadAll() {
for (const item of emojiArray) {
await downloadImage(item);
}
console.log('All downloads completed!');
}
downloadAll();
表情下载完,后面就好办了——交给 AI 🤖。
3、组件实现
大概思路是有的,本质就是一个 contenteditable 的 div,组件实现完全让 AI 完成,但需要考虑一些特殊情况,要给出明确期望:
1️⃣ 光标位置管理
- 长度达到限制时裁剪字符,统一将光标设置到末尾,防止位置异常(element-plus输入框效果如此)
- 点击空白区域自动定位到末尾
2️⃣ 文本与图片双向转换
文本 → 图片
- 使用防抖(200ms)避免频繁触发
- 仅转换光标前的内容,光标后的内容保持不变
- 通过正则匹配
[xxx]格式进行转换 - 关键:不在用户输入过程中转换,避免干扰输入体验
图片 → 文本
- 文本节点直接提取
textContent - 图片节点提取
data-alt属性转为文本 - 过滤零宽字符
\u200B
3️⃣ 字符长度计算
精确计算混合内容长度:中文 1 个字符,英文/数字 0.5 个,表情 1 个。
4️⃣ 输入法兼容
处理中文输入法的组合事件:compositionstart → compositionupdate → compositionend,避免在拼音输入阶段触发转换。
5️⃣ 删除表情
确保删除时完整移除表情图片节点,不留残留。
最终效果:
![]()
💡总结
借助 AI 辅助开发,核心在于:告知 AI 需要处理的特殊情况,以及不断测试和调优。
只要明确需求规范和边界情况,就能高效实现功能。怎么有种从开发转测试的感觉。