今天在項目中,遇到了一個需求:將后臺返回的 [微笑] [撇嘴] 這種表情文字轉(zhuǎn)化為emoji表情...
表情轉(zhuǎn)化組件
// 定義所有表情文字
const word = ["微笑","撇嘴","色","發(fā)呆","得意" ...]
...
class Face extends React.PureComponent<Props, State> {
...
// 文字轉(zhuǎn)圖片接口
public insetFace(content: string): any {
if (!content || content.trim().length <= 0) {
return "";
}
let reg = /\[.*?\]/gi; //匹配表情正則
let rgArr = content.match(reg);
if (rgArr && rgArr.length > 0) {
rgArr.map((item: string) => {
let w = item.replace(/^\[|\]$/gi, "");
if (word.indexOf(w) != -1) {
content = content.replace(
item,
`<img class="emoji-item" src='${require("@img/face/face_" +word.indexOf(w) +".png")}'/>`
);
}
});
}
return content;
}
...
export const insetFace = FaceFun.insetFace;
類似于微信聊天,由于數(shù)據(jù)中既有 圖片 又有 文字+表情,所以這里要判斷是否是圖片this.isImg,是圖片的話就將地址賦值給src,若包含[微笑]這種emoji標簽,則需要引用insetFace這個接口,將data傳入進行轉(zhuǎn)換。
index.tsx
// 引入
import { insetFace } from "@component/face";
...
export default class ScriptBox extends ListPage<Props, State> {
constructor(props: Props) {
super(props);
}
...
// 判斷是否圖片函數(shù)
isImg(data: string) {
if (!data || data.length <= 0) {
return false;
}
let reg = /^https?:\/\/.*?.(png|jpg|jpeg)$/gi;
return reg.test(data);
}
...
{targetData &&targetData.scriptmessage &&targetData.scriptmessage.length > 0 &&
targetData.scriptmessage.map(
(v: any, i: number) => {
return (
<React.Fragment key={i}>
{v[0] && v[0].length > 0 && (
<div className="detail-item">
<div className="name"> A:</div>
<div className="content">
{this.isImg(v[0]) ?
(<img className="detail_img" src={v[0]} />) : ( insetFace(v[0]) )}
</div>
</div>
)}
...
...
</React.Fragment>
}
渲染出來結(jié)果是下圖:

失敗.png
總結(jié)原因:
這里將insetFace接口返回的<img class="emoji-item" src='${require("@img/face/face_" +word.indexOf(w) +".png")}'/>當作字符串來渲染。類似于JS的innerText,而需求則是將其作用為innerHTML。
所以這里需要引入react的一個apidangerouslySetInnerHTML
dangerouslySetInnerHTML api官方文檔介紹
dangerouslySetInnerHTML是 React 為瀏覽器 DOM 提供innerHTML的替換方案。通常來講,使用代碼直接設(shè)置 HTML 存在風險,因為很容易無意中使用戶暴露于跨站腳本(XSS)的攻擊。因此,你可以直接在 React 中設(shè)置 HTML,但當你想設(shè)置dangerouslySetInnerHTML時,需要向其傳遞包含 key 為__html的對象,以此來警示你。例如:
function createMarkup() {
return {__html: 'First · Second'};
}
function MyComponent() {
return <div dangerouslySetInnerHTML={createMarkup()} />;
}
在此次應用中,將index.tsx中的代碼改為:
...
...
{targetData.scriptmessage.map(
(v: any, i: number) => {
return (
<React.Fragment key={i}>
{v[0] && v[0].length > 0 && (
<div className="detail-item">
<div className="name">A:</div>
<div className="content"
dangerouslySetInnerHTML={{
__html: this.isImg(v[0])
? `<img class="detail_img" src=${v[0]} />`
: insetFace( v[0] )
}}
>
</div>
</div>
)}
</React.Fragment>
...
...
結(jié)果:

成功.png
注意:
-
dangerouslySetInnerHTML的語法:第一層{ }代表JSX語法,第二層{ }是一個__html:string的鍵值對 - 最初未用反引號包裹
<img className="detail_img" src=${v[0]} />結(jié)果渲染的全是[object Object],想了半天才反應過來__html:string - 用反引號包裹的
html代碼不再是JSX語法,所以clasName要改為class