对html字符串进行解析展示,包括消毒和其他reactDom操作
先对html字符串进行消毒处理
import sanitizeHtml from 'sanitize-html';
const sanitizeHtmlOptiosn = {
allowedTags: [
"address", "article", "aside", "footer", "header", "h1", "h2", "h3", "h4",
"h5", "h6", "hgroup", "main", "nav", "section", "blockquote", "dd", "div",
"dl", "dt", "figcaption", "figure", "hr", "li", "main", "ol", "p", "pre",
"ul", "a", "abbr", "b", "bdi", "bdo", "br", "cite", "code", "data", "dfn",
"em", "i", "kbd", "mark", "q", "rb", "rp", "rt", "rtc", "ruby", "s", "samp",
"small", "span", "strong", "sub", "sup", "time", "u", "var", "wbr", "caption",
"col", "colgroup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "font",
"img", "video", "audio",
],
disallowedTagsMode: 'discard',
allowedAttributes: {
font: [ 'color' ],
a: [ 'href', 'name', 'target' ],
img: [ 'src', 'srcset', 'alt', 'title', 'width', 'height', 'loading' ],
video: ['preload', 'controls', 'src'],
audio: ['src'],
'*': ['style', 'class', 'id', 'data-kc'],
},
selfClosing: [ 'img', 'br', 'hr', 'area', 'base', 'basefont', 'input', 'link', 'meta' ],
allowedSchemes: [ 'http', 'https', 'ftp', 'mailto', 'tel', 'message', 'copy', 'dingtalk', 'dtmd' ],
allowedSchemesByTag: {},
allowedSchemesAppliedToAttributes: [ 'href', 'src', 'cite' ],
allowProtocolRelative: true,
enforceHtmlBoundary: false
}
const str1 = sanitizeHtml(str, sanitizeHtmlOptiosn);
对消毒的内容解析展示
import parse, { domToReact, HTMLReactParserOptions, Element } from 'html-react-parser';
import { PhotoProvider, PhotoView } from 'react-photo-view';
import 'react-photo-view/dist/react-photo-view.css';
const options: HTMLReactParserOptions = {
replace: domNode => {
if (domNode instanceof Element && domNode.attribs) {
const { attribs, children, name } = domNode;
if (name === 'a') {
return (
<a style={{ color: '#4d96f8' }} href={attribs.href} target='_blank'>
{domToReact(children, plainOptions)}
</a>
)
}
if (name === 'img') {
return (
<PhotoProvider photoClosable={true} bannerVisible={false}>
<PhotoView src={attribs.src}>
<img src={attribs.src} style={{ width: '100%', height: 'auto', objectFit: 'contain' }} />
</PhotoView>
</PhotoProvider>
)
}
if (name === 'mark') {
return <mark style={{ padding: 0 }}>{domToReact(children, plainOptions)}</mark>;
}
}
}
};
return parse(str1, options);
附上完整代码
import React from 'react';
import sanitizeHtml from 'sanitize-html';
import parse, { domToReact, HTMLReactParserOptions, Element } from 'html-react-parser';
import { PhotoProvider, PhotoView } from 'react-photo-view';
import 'react-photo-view/dist/react-photo-view.css';
const sanitizeHtmlOptiosn = {
allowedTags: [
"address", "article", "aside", "footer", "header", "h1", "h2", "h3", "h4",
"h5", "h6", "hgroup", "main", "nav", "section", "blockquote", "dd", "div",
"dl", "dt", "figcaption", "figure", "hr", "li", "main", "ol", "p", "pre",
"ul", "a", "abbr", "b", "bdi", "bdo", "br", "cite", "code", "data", "dfn",
"em", "i", "kbd", "mark", "q", "rb", "rp", "rt", "rtc", "ruby", "s", "samp",
"small", "span", "strong", "sub", "sup", "time", "u", "var", "wbr", "caption",
"col", "colgroup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "font",
"img", "video", "audio",
],
disallowedTagsMode: 'discard',
allowedAttributes: {
font: [ 'color' ],
a: [ 'href', 'name', 'target' ],
img: [ 'src', 'srcset', 'alt', 'title', 'width', 'height', 'loading' ],
video: ['preload', 'controls', 'src'],
audio: ['src'],
'*': ['style', 'class', 'id', 'data-kc'],
},
selfClosing: [ 'img', 'br', 'hr', 'area', 'base', 'basefont', 'input', 'link', 'meta' ],
allowedSchemes: [ 'http', 'https', 'ftp', 'mailto', 'tel', 'message', 'copy', 'dingtalk', 'dtmd' ],
allowedSchemesByTag: {},
allowedSchemesAppliedToAttributes: [ 'href', 'src', 'cite' ],
allowProtocolRelative: true,
enforceHtmlBoundary: false
}
export function parseRichText(text: string) {
const options: HTMLReactParserOptions = {
replace: domNode => {
if (domNode instanceof Element && domNode.attribs) {
const { attribs, children, name } = domNode;
if (name === 'a') {
return (
<a style={{ color: '#4d96f8' }} href={attribs.href} target="_blank">
{domToReact(children, detailOptions)}
</a>
)
}
if (name === 'img') {
return (
<PhotoProvider photoClosable={true} bannerVisible={false}>
<PhotoView src={attribs.src}>
<img src={attribs.src} style={{ width: '100%', height: 'auto', objectFit: 'contain' }} />
</PhotoView>
</PhotoProvider>
)
}
if (name === 'mark') {
return <mark style={{ padding: 0 }}>{domToReact(children, detailOptions)}</mark>;
}
}
}
};
return parse(sanitizeHtml(text, sanitizeHtmlOptiosn), options);
}