当前位置: 首页 > news >正文

【前端】Twemoji(Twitter Emoji)

目录

  • 注意
  • 使用
    • Vue / React 项目
  • 验证

  • Twemoji 的作用:
    Twemoji 会把你网页/应用中的 Emoji 字符(如 😄)自动替换为 Twitter 风格的图片(SVG/PNG);
    它不依赖系统字体,因此在 Android、Windows、macOS、iOS 等系统上都能显示统一的视觉效果;但显示出来的 不是 Apple emoji的风格,而是 Twitter 的风格。
场景是否能解决
安卓显示苹果风格 Emoji无法使用 Apple 的图形❌
安卓系统不支持新 Emoji,无法显示用图像替代,兼容所有新旧设备
各系统 Emoji 风格不统一Twemoji 保证所有平台一致
不依赖用户系统字体
可自定义大小、样式、加载策略

注意

npm 包用的也是maxcdn的地址,但是maxcdn已经崩溃了。
之后所有的twemoji.parse都应该修改base

import twemoji from 'twemoji';export default {install(app: any) {app.directive('twemoji', {mounted(el: HTMLElement) {twemoji.parse(el, {base: 'https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/'});},updated(el: HTMLElement) {twemoji.parse(el,{base: 'https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/'});}});}
};
//这样就可以正常加载了

国内源https://cdn.jsdmirror.com/gh/twitter/twemoji@14.0.2/assets/
cdn.jsdmirror.com

  • emoji显示大小调整
    Twemoji 默认插入的 <img> 标签没有限制尺寸,导致显示时占用过多空间。
/* 全局限制 twemoji img 大小 */
img.emoji, img.emoji-svg {display:inline-block;width: 1em;height: 1em;vertical-align: -0.1em; /* 调整与文字基线对齐 */
}

使用

  1. CDN引入
<script src="https://twemoji.maxcdn.com/v/latest/twemoji.min.js"></script>
  1. 在页面加载后执行解析
<script>document.addEventListener('DOMContentLoaded', function () {twemoji.parse(document.body, {folder: 'svg', // svg 或 72x72(PNG 图片)ext: '.svg'    // 或 '.png'});});
</script>

Vue / React 项目

  • 安装
npm install twemoji
  • 全局应用 Twemoji(也就是全站的 Emoji 都自动替换为 Twemoji 风格)方案:
框架推荐方式说明
Vue 3创建自定义指令 / 全局组件自动处理所有含 Emoji 的元素
React使用全局解析函数(如 HOC 或 Layout hook)在渲染后全局替换 DOM 中 Emoji
//vue 全局应用 
//(用于 layout组件 或 app.vue)
<script setup>
import { onMounted } from 'vue';
import twemoji from 'twemoji';onMounted(() => {twemoji.parse(document.body, {folder: 'svg',ext: '.svg'});
});
</script>//react全局应用
//方法1 在 Layout / App.jsx 中统一解析
import React, { useEffect } from 'react';
import twemoji from 'twemoji';function App() {useEffect(() => {twemoji.parse(document.body, {folder: 'svg',ext: '.svg'});}, []);//只会在组件首次渲染(挂载)到 DOM 之后执行一次,return (<div><h1>Hello React 😄❤️🎉</h1>{/* 其他页面组件 */}</div>);
}
export default App;//方法2 创建高阶组件(HOC)包裹需要解析的组件
//接收组件 返回包装好的新组件
import twemoji from 'twemoji';
import { useEffect, useRef } from 'react';export function withTwemoji(Component) {return function Wrapped(props) {const ref = useRef();useEffect(() => {if (ref.current) {//ref.current 指的就是 useRef 返回的 ref 对象所实际引用的值twemoji.parse(ref.current, {folder: 'svg',ext: '.svg'});}}, []);return (<div ref={ref}><Component {...props} /></div>);};
}
//需要使用的组件直接使用:
const TwemojiPage = withTwemoji(MyPageComponent);

twemoji.parse() 的作用是:它会扫描传入的 DOM 元素(在这里是 ref.current 所引用的元素)内部的文本内容,找到其中的 Unicode Emoji 字符,然后将这些字符替换成 <img> 标签,而<img>标签的 src 属性指向 Twemoji 的 SVG 图片。ref.current 这个变量本身(它存储的内存地址)没有改变,它依然指向同一个 div 元素。但这个 div 元素内部的子节点和内容被 twemoji.parse 方法修改了。

  • 但vue全局应用的方法可能出现问题:没有生效
    解析时 DOM 内容还没真正渲染完,App.vue 的 onMounted() 仅保证 Vue 根组件挂载,但此时子组件内容可能尚未完全渲染进 DOM;
    解决方法有两种
  1. 用 Layout 页面来做解析(推荐用于中大型项目)
    不要在 App.vue 直接解析,而是把解析放到页面 Layout 或页面组件(如 MainLayout.vue 或 HomePage.vue)中的 onMounted() 中,这样确保 Emoji 渲染后再替换
    推荐在主页面区域加个 id=“main-content” 来明确定位,不要直接操作整个 document.body。
    保证这个区域包裹需要解析的区域
import { onMounted, ref } from 'vue';
import twemoji from 'twemoji';onMounted(() => {const el = document.getElementById('main-content'); // 指定 DOM 区域if (el) {twemoji.parse(el, { folder: 'svg', ext: '.svg' });}
});//最好是使用nextTick 保证获取到的是最新内容
onMounted(() => {nextTick(() => {//等待 Vue 完成本轮 DOM 更新后再执行代码,确保你访问到的是已经渲染进 DOM 的最终结果。if (contentRef.value) {twemoji.parse(contentRef.value, {folder: 'svg',ext: '.svg'});}});//如果带emoji的内容是通过异步加载或父组件传入,也可能不会生效
//Vue 渲染晚于 onMounted,那 Twemoji 在执行时内容还没出现在 DOM。
//解决方法:用 watch 监听变化后再解析
import { watch } from 'vue';
watch(content, () => {nextTick(() => {twemoji.parse(emojiBox.value, { folder: 'svg', ext: '.svg' });});
});//如果内容使用了 v-html,Vue 不会触发 DOM 更新钩子,
// 则twemoji.parse 不会自动处理
  1. 封装成全局自定义指令(最推荐)
    使用 v-twemoji 指令来保证解析的是已经渲染完的 DOM 元素
// plugins/twemoji.js
import twemoji from 'twemoji';export default {install(app) {app.directive('twemoji', {mounted(el) {twemoji.parse(el, { folder: 'svg', ext: '.svg' });},updated(el) {twemoji.parse(el, { folder: 'svg', ext: '.svg' });}});}
};
//在 App.vue、Layout.vue 或任意组件这样使用
<template><div v-twemoji>Hello 😊🎉</div>
</template>
  • 部分应用
//部分应用
//vue
<template><div ref="emojiContainer">{{ message }}</div>
</template><script setup>
import { onMounted, ref } from 'vue';
import twemoji from 'twemoji';const emojiContainer = ref(null);
const message = '你好 Vue 😊❤️🎉';onMounted(() => {twemoji.parse(emojiContainer.value, {folder: 'svg',ext: '.svg'});
});
</script>//react
import React, { useEffect, useRef } from 'react';
import twemoji from 'twemoji';const EmojiText = ({ text }) => {const ref = useRef();useEffect(() => {twemoji.parse(ref.current, {folder: 'svg',ext: '.svg'});}, []);return <div ref={ref}>{text}</div>;
};export default function App() {return <EmojiText text="Hello 😄🎉 from React!" />;
}

验证

使用浏览器开发者工具,检查是否 emoji 被替换为<img>,src来自twemoji…cdn…地址

相关文章:

  • LeetCode 215:数组中的第K个最大元素 - 两种高效解法详解
  • 力扣-最长回文子串
  • HTB-Planning
  • PnP(Perspective-n-Point)算法 | 用于求解已知n个3D点及其对应2D投影点的相机位姿
  • Python基础教程:控制流与函数入门 - 第4-6天
  • 【网络入侵检测】基于Suricata源码分析FlowWorker实现
  • 智能仓储落地:机器人如何通过自动化减少仓库操作失误?
  • DeepSeek - 尝试一下GitHub Models中的DeepSeek
  • EasyRTC音视频实时通话助力微信小程序:打造低延迟、高可靠的VoIP端到端呼叫解决方案
  • 【ConvLSTM第二期】模拟视频帧的时序建模(Python代码实现)
  • Text-to-SQL评估体系:从Spider 1.0数据集到2.0框架的跨越与革新
  • HOW - 简历和求职面试宝典(八)
  • 【春秋云镜】CVE-2022-26965 靶场writeup
  • 江西某石灰石矿边坡自动化监测
  • 【Hive 运维实战】一键管理 Hive 服务:Metastore 与 HiveServer2 控制脚本开发与实践
  • LeetCode Hot100(多维动态规划)
  • 基于vue框架的独居老人上门护理小程序的设计r322q(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • 前端面试核心考点全解析
  • 华为OD机试真题——告警抑制(2025A卷:100分)Java/python/JavaScript/C/C++/GO最佳实现
  • .NET 开源工业视觉系统 OpenIVS 快速搭建自动化检测平台
  • 营销策划网站/免费信息发布平台网站
  • 腾讯建设网站视频下载/竞价推广员月挣多少
  • 装修网站怎么做的好/如何推广自己的店铺?
  • asp.net做网站源代码/给网站做seo的价格
  • 吉安哪里做网站/电商运营培训课程
  • 软件工程考研学校推荐/丈哥seo博客工具