今天在项目中,遇到了一个需求:将后台返回的 [微笑] [撇嘴] 这种表情文字转化为emoji
表情...
表情转化组件
// 定义所有表情文字
const word = ["微笑","撇嘴","色","发呆","得意" ...]
...
class Face extends React.PureComponent<Props, State> {
...
// 文字转图片接口
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;
类似于微信聊天,由于数据中既有 图片 又有 文字+表情,所以这里要判断是否是图片this.isImg
,是图片的话就将地址赋值给src,若包含[微笑]
这种emoji
标签,则需要引用insetFace这个接口,将data传入进行转换。
index.tsx
// 引入
import { insetFace } from "@component/face";
...
export default class ScriptBox extends ListPage<Props, State> {
constructor(props: Props) {
super(props);
}
...
// 判断是否图片函数
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>
}
渲染出来结果是下图:
总结原因:
这里将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
的替换方案。通常来讲,使用代码直接设置 HTML 存在风险,因为很容易无意中使用户暴露于跨站脚本(XSS)的攻击。因此,你可以直接在 React 中设置 HTML,但当你想设置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>
...
...
结果:
注意:
-
dangerouslySetInnerHTML
的语法:第一层{ }代表JSX语法,第二层{ }是一个__html:string
的键值对 - 最初未用反引号包裹
<img className="detail_img" src=${v[0]} />
结果渲染的全是[object Object],想了半天才反应过来__html:string
- 用反引号包裹的
html
代码不再是JSX语法,所以clasName
要改为class