目錄
- 注意
- 使用
- 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; /* 調整與文字基線對齊 */
}
使用
- CDN引入
<script src="https://twemoji.maxcdn.com/v/latest/twemoji.min.js"></script>
- 在頁面加載后執行解析
<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;
解決方法有兩種
- 用 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 不會自動處理
- 封裝成全局自定義指令(最推薦)
使用 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…地址